Browse Source

Merge pull request #664 from SixLabors/colorspace-transforms

WIP Colorspace Transforms
af/merge-core
James Jackson-South 8 years ago
committed by GitHub
parent
commit
6204d76684
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 143
      src/ImageSharp/ColorSpaces/CieLab.cs
  2. 151
      src/ImageSharp/ColorSpaces/CieLch.cs
  3. 149
      src/ImageSharp/ColorSpaces/CieLchuv.cs
  4. 146
      src/ImageSharp/ColorSpaces/CieLuv.cs
  5. 117
      src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
  6. 132
      src/ImageSharp/ColorSpaces/CieXyy.cs
  7. 132
      src/ImageSharp/ColorSpaces/CieXyz.cs
  8. 144
      src/ImageSharp/ColorSpaces/Cmyk.cs
  9. 105
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs
  10. 324
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
  11. 317
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
  12. 319
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
  13. 294
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
  14. 281
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
  15. 305
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
  16. 281
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
  17. 281
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
  18. 281
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
  19. 285
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
  20. 290
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
  21. 280
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
  22. 280
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
  23. 260
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
  24. 114
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs
  25. 55
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs
  26. 21
      src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs
  27. 22
      src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs
  28. 49
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs
  29. 12
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs
  30. 14
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs
  31. 33
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
  32. 12
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs
  33. 14
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs
  34. 28
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
  35. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs
  36. 6
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs
  37. 45
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs
  38. 19
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs
  39. 28
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs
  40. 21
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
  41. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs
  42. 51
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs
  43. 28
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs
  44. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs
  45. 16
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
  46. 33
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs
  47. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs
  48. 29
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs
  49. 29
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs
  50. 24
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs
  51. 97
      src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs
  52. 21
      src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
  53. 46
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs
  54. 35
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs
  55. 24
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs
  56. 32
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs
  57. 31
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs
  58. 24
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs
  59. 98
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs
  60. 33
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs
  61. 36
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaCompanding.cs
  62. 65
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
  63. 37
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LCompanding.cs
  64. 31
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs
  65. 36
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020Companding.cs
  66. 31
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs
  67. 35
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709Companding.cs
  68. 31
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs
  69. 83
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs
  70. 35
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbCompanding.cs
  71. 31
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs
  72. 56
      src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs
  73. 125
      src/ImageSharp/ColorSpaces/Hsl.cs
  74. 181
      src/ImageSharp/ColorSpaces/Hsv.cs
  75. 142
      src/ImageSharp/ColorSpaces/HunterLab.cs
  76. 28
      src/ImageSharp/ColorSpaces/IAlmostEquatable.cs
  77. 18
      src/ImageSharp/ColorSpaces/IColorVector.cs
  78. 37
      src/ImageSharp/ColorSpaces/ICompanding.cs
  79. 2
      src/ImageSharp/ColorSpaces/Illuminants.cs
  80. 163
      src/ImageSharp/ColorSpaces/LinearRgb.cs
  81. 134
      src/ImageSharp/ColorSpaces/Lms.cs
  82. 172
      src/ImageSharp/ColorSpaces/Rgb.cs
  83. 42
      src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs
  84. 127
      src/ImageSharp/ColorSpaces/YCbCr.cs
  85. 34
      src/ImageSharp/Common/Extensions/ComparableExtensions.cs
  86. 78
      src/ImageSharp/Common/Extensions/Vector4Extensions.cs
  87. 44
      src/ImageSharp/Common/Helpers/Guard.cs
  88. 22
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  89. 4
      src/ImageSharp/Common/Helpers/InliningOptions.cs
  90. 4
      src/ImageSharp/PixelFormats/README.md
  91. 15
      src/ImageSharp/PixelFormats/Rgb24.cs
  92. 113
      src/ImageSharp/PixelFormats/Rgba32.cs
  93. 2
      tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs
  94. 40
      tests/ImageSharp.Benchmarks/General/Pow.cs
  95. 3
      tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
  96. 46
      tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs
  97. 44
      tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs
  98. 44
      tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs
  99. 44
      tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs
  100. 43
      tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs

143
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>
public 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 usually 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 usually 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 usually 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.
@ -32,9 +49,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="a">The a (green - magenta) component.</param>
/// <param name="b">The b (blue - yellow) component.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLab(float l, float a, float b)
: this(new Vector3(l, a, b), DefaultWhitePoint)
: this(l, a, b, DefaultWhitePoint)
{
}
@ -45,7 +62,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="a">The a (green - magenta) component.</param>
/// <param name="b">The b (blue - yellow) component.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLab(float l, float a, float b, CieXyz whitePoint)
: this(new Vector3(l, a, b), whitePoint)
{
@ -56,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, a, b components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLab(Vector3 vector)
: this(vector, DefaultWhitePoint)
{
@ -67,126 +84,62 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, a, b components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLab(Vector3 vector, CieXyz whitePoint)
: this()
{
this.backingVector = vector;
// Not clamping as documentation about this space only indicates "usual" ranges
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "CieLab [Empty]"
: $"CieLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]";
}
public override string ToString() => $"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})";
/// <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)]
[MethodImpl(InliningOptions.ShortMethod)]
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;
}
}
}

151
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,8 +11,11 @@ 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>
public readonly struct CieLch : IEquatable<CieLch>
{
private static readonly Vector3 Min = new Vector3(0, -200, 0);
private static readonly Vector3 Max = new Vector3(100, 200, 360);
/// <summary>
/// D50 standard illuminant.
/// Used when reference white is not specified explicitly.
@ -21,9 +23,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 200.</remarks>
/// </summary>
public readonly float C;
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary>
private readonly Vector3 backingVector;
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.
@ -32,9 +52,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="c">The chroma, relative saturation.</param>
/// <param name="h">The hue in degrees.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLch(float l, float c, float h)
: this(new Vector3(l, c, h), DefaultWhitePoint)
: this(l, c, h, DefaultWhitePoint)
{
}
@ -45,7 +65,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="c">The chroma, relative saturation.</param>
/// <param name="h">The hue in degrees.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLch(float l, float c, float h, CieXyz whitePoint)
: this(new Vector3(l, c, h), whitePoint)
{
@ -56,7 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, c, h components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLch(Vector3 vector)
: this(vector, DefaultWhitePoint)
{
@ -67,129 +87,64 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, c, h components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLch(Vector3 vector, CieXyz whitePoint)
: this()
{
this.backingVector = vector;
vector = Vector3.Clamp(vector, Min, Max);
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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieLch left, CieLch right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieLch left, CieLch right) => left.Equals(right);
/// <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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "CieLch [Empty]"
: $"CieLch [ L={this.L:#0.##}, C={this.C:#0.##}, H={this.H:#0.##}]";
}
public override string ToString() => $"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})";
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
return obj is CieLch other && this.Equals(other);
}
[MethodImpl(InliningOptions.ShortMethod)]
public override bool Equals(object obj) => obj is CieLch other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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>
@ -197,7 +152,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// A value ranging from 0 to 100.
/// </remarks>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public float Saturation()
{
float result = 100 * (this.C / this.L);

149
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;
@ -10,10 +9,13 @@ namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// 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"/>
/// <see href="https://en.wikipedia.org/wiki/CIELAB_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC"/>
/// </summary>
internal readonly struct CieLchuv : IColorVector, IEquatable<CieLchuv>, IAlmostEquatable<CieLchuv, float>
public readonly struct CieLchuv : IEquatable<CieLchuv>
{
private static readonly Vector3 Min = new Vector3(0, -200, 0);
private static readonly Vector3 Max = new Vector3(100, 200, 360);
/// <summary>
/// D50 standard illuminant.
/// Used when reference white is not specified explicitly.
@ -21,9 +23,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 200.</remarks>
/// </summary>
public readonly float C;
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary>
private readonly Vector3 backingVector;
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="CieLchuv"/> struct.
@ -32,9 +52,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="c">The chroma, relative saturation.</param>
/// <param name="h">The hue in degrees.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv(float l, float c, float h)
: this(new Vector3(l, c, h), DefaultWhitePoint)
: this(l, c, h, DefaultWhitePoint)
{
}
@ -45,9 +65,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="c">The chroma, relative saturation.</param>
/// <param name="h">The hue in degrees.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv(float l, float c, float h, CieXyz whitePoint)
: this(new Vector3(l, c, h), whitePoint)
: this(new Vector3(l, c, h), whitePoint)
{
}
@ -56,7 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, c, h components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv(Vector3 vector)
: this(vector, DefaultWhitePoint)
{
@ -67,127 +87,62 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, c, h components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv(Vector3 vector, CieXyz whitePoint)
: this()
{
this.backingVector = vector;
vector = Vector3.Clamp(vector, Min, Max);
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/>
public override string ToString()
{
return this.Equals(default)
? "CieLchuv [Empty]"
: $"CieLchuv [ L={this.L:#0.##}, C={this.C:#0.##}, H={this.H:#0.##}";
}
public override string ToString() => $"CieLchuv({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})";
/// <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)]
[MethodImpl(InliningOptions.ShortMethod)]
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>
@ -195,7 +150,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// A value ranging from 0 to 100.
/// </remarks>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public float Saturation()
{
float result = 100 * (this.C / this.L);

146
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>
public 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.
@ -34,9 +51,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="u">The blue-yellow chromaticity coordinate of the given whitepoint.</param>
/// <param name="v">The red-green chromaticity coordinate of the given whitepoint.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLuv(float l, float u, float v)
: this(new Vector3(l, u, v), DefaultWhitePoint)
: this(l, u, v, DefaultWhitePoint)
{
}
@ -47,7 +64,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="u">The blue-yellow chromaticity coordinate of the given whitepoint.</param>
/// <param name="v">The red-green chromaticity coordinate of the given whitepoint.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLuv(float l, float u, float v, CieXyz whitePoint)
: this(new Vector3(l, u, v), whitePoint)
{
@ -58,7 +75,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, u, v components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLuv(Vector3 vector)
: this(vector, DefaultWhitePoint)
{
@ -69,126 +86,61 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, u, v components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieLuv(Vector3 vector, CieXyz whitePoint)
: this()
{
this.backingVector = vector;
// Not clamping as documentation about this space only indicates "usual" ranges
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "CieLuv [ Empty ]"
: $"CieLuv [ L={this.L:#0.##}, U={this.U:#0.##}, V={this.V:#0.##} ]";
}
public override string ToString() => $"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})";
/// <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)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
}
}

117
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>
public 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,85 +26,54 @@ 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(InliningOptions.ShortMethod)]
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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) => left.Equals(right);
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) => !left.Equals(right);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
}
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() => HashHelpers.Combine(this.X.GetHashCode(), this.Y.GetHashCode());
/// <inheritdoc/>
public override string ToString()
{
return this.Equals(default)
? "CieXyChromaticityCoordinates [Empty]"
: $"CieXyChromaticityCoordinates [ X={this.X:#0.##}, Y={this.Y:#0.##}]";
}
public override string ToString() => $"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})";
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieXyChromaticityCoordinates other && this.Equals(other);
}
public override bool Equals(object obj) => 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;
}
/// <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;
}
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyChromaticityCoordinates other) => this.X.Equals(other.X) && this.Y.Equals(other.Y);
}
}

132
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>
public 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.
@ -25,130 +37,72 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="x">The x chroma component.</param>
/// <param name="y">The y chroma component.</param>
/// <param name="yl">The y luminance component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyy(float x, float y, float yl)
: this(new Vector3(x, y, yl))
{
// Not clamping as documentation about this space only indicates "usual" ranges
this.X = x;
this.Y = y;
this.Yl = yl;
}
/// <summary>
/// Initializes a new instance of the <see cref="CieXyy"/> struct.
/// </summary>
/// <param name="vector">The vector representing the x, y, Y components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyy(Vector3 vector)
: this()
{
// Not clamping as documentation about this space seems to indicate "usual" ranges
this.backingVector = vector;
// Not clamping as documentation about this space only indicates "usual" ranges
this.X = vector.X;
this.Y = vector.Y;
this.Yl = vector.Z;
}
/// <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;
}
/// <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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "CieXyy [ Empty ]"
: $"CieXyy [ X={this.X:#0.##}, Y={this.Y:#0.##}, Yl={this.Yl:#0.##} ]";
}
public override string ToString() => $"CieXyy({this.X:#0.##}, {this.Y:#0.##}, {this.Yl:#0.##})";
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is CieXyy other && this.Equals(other);
}
public override bool Equals(object obj) => obj is CieXyy other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
}
}

132
src/ImageSharp/ColorSpaces/CieXyz.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 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>
public 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>
private readonly Vector3 backingVector;
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>
public readonly float Z;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyz"/> struct.
@ -25,7 +37,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="x">X is a mix (a linear combination) of cone response curves chosen to be nonnegative</param>
/// <param name="y">The y luminance component.</param>
/// <param name="z">Z is quasi-equal to blue stimulation, or the S cone of the human eye.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz(float x, float y, float z)
: this(new Vector3(x, y, z))
{
@ -38,116 +50,62 @@ namespace SixLabors.ImageSharp.ColorSpaces
public CieXyz(Vector3 vector)
: 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;
// Not clamping as documentation about this space only indicates "usual" ranges
this.X = vector.X;
this.Y = vector.Y;
this.Z = vector.Z;
}
/// <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;
}
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CieXyz left, CieXyz right)
{
return left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieXyz left, CieXyz right) => left.Equals(right);
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieXyz left, CieXyz right)
{
return !left.Equals(right);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieXyz left, CieXyz right) => !left.Equals(right);
/// <summary>
/// Returns a new <see cref="Vector3"/> representing this instance.
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Vector3 ToVector3() => new Vector3(this.X, this.Y, this.Z);
/// <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.Z.GetHashCode());
}
/// <inheritdoc/>
public override string ToString()
{
return this.Equals(default)
? "CieXyz [ Empty ]"
: $"CieXyz [ X={this.X:#0.##}, Y={this.Y:#0.##}, Z={this.Z:#0.##} ]";
}
public override string ToString() => $"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})";
/// <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)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
}
}

144
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,151 +10,108 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents an CMYK (cyan, magenta, yellow, keyline) color.
/// </summary>
internal readonly struct Cmyk : IEquatable<Cmyk>, IAlmostEquatable<Cmyk, float>
public readonly struct Cmyk : IEquatable<Cmyk>
{
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private readonly Vector4 backingVector;
private static readonly Vector4 Min = Vector4.Zero;
private static readonly Vector4 Max = Vector4.One;
/// <summary>
/// Initializes a new instance of the <see cref="Cmyk"/> struct.
/// Gets the cyan color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
/// <param name="c">The cyan component.</param>
/// <param name="m">The magenta component.</param>
/// <param name="y">The yellow component.</param>
/// <param name="k">The keyline black component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cmyk(float c, float m, float y, float k)
: this(new Vector4(c, m, y, k))
{
}
public readonly float C;
/// <summary>
/// Initializes a new instance of the <see cref="Cmyk"/> struct.
/// Gets the magenta color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
/// <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);
}
public readonly float M;
/// <summary>
/// Gets the cyan color component.
/// Gets the yellow color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float C
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
public readonly float Y;
/// <summary>
/// Gets the magenta color component.
/// Gets the keyline black color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float M
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
public readonly float K;
/// <summary>
/// Gets the yellow color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// Initializes a new instance of the <see cref="Cmyk"/> struct.
/// </summary>
public float Y
/// <param name="c">The cyan component.</param>
/// <param name="m">The magenta component.</param>
/// <param name="y">The yellow component.</param>
/// <param name="k">The keyline black component.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Cmyk(float c, float m, float y, float k)
: this(new Vector4(c, m, y, k))
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <summary>
/// Gets the keyline black color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// Initializes a new instance of the <see cref="Cmyk"/> struct.
/// </summary>
public float K
/// <param name="vector">The vector representing the c, m, y, k components.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public Cmyk(Vector4 vector)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.W;
vector = Vector4.Clamp(vector, Min, Max);
this.C = vector.X;
this.M = vector.Y;
this.Y = 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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(Cmyk left, Cmyk right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "Cmyk [Empty]"
: $"Cmyk [ C={this.C:#0.##}, M={this.M:#0.##}, Y={this.Y:#0.##}, K={this.K:#0.##}]";
}
public override string ToString() => $"Cmyk({this.C:#0.##}, {this.M:#0.##}, {this.Y:#0.##}, {this.K:#0.##})";
/// <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)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
}
}

105
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs

@ -1,151 +1,130 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Performs chromatic adaptation on the various color spaces.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
/// <summary>
/// Performs chromatic adaptation of given <see cref="CieXyz"/> color.
/// Target white point is <see cref="WhitePoint"/>.
/// Target white point is <see cref="ColorSpaceConverterOptions.WhitePoint"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <param name="sourceWhitePoint">The white point to adapt for</param>
/// <param name="sourceWhitePoint">The source white point.</param>
/// <returns>The adapted color</returns>
public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint)
public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint) => this.Adapt(color, sourceWhitePoint, this.whitePoint);
/// <summary>
/// Performs chromatic adaptation of given <see cref="CieXyz"/> color.
/// Target white point is <see cref="ColorSpaceConverterOptions.WhitePoint"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <param name="sourceWhitePoint">The source white point.</param>
/// <param name="targetWhitePoint">The target white point.</param>
/// <returns>The adapted color</returns>
public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint, in CieXyz targetWhitePoint)
{
if (!this.IsChromaticAdaptationPerformed)
if (!this.performChromaticAdaptation || sourceWhitePoint.Equals(targetWhitePoint))
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
return color;
}
return this.ChromaticAdaptation.Transform(color, sourceWhitePoint, this.WhitePoint);
return this.chromaticAdaptation.Transform(color, sourceWhitePoint, targetWhitePoint);
}
/// <summary>
/// Adapts <see cref="CieLab"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>.
/// Adapts <see cref="CieLab"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetLabWhitePoint"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public CieLab Adapt(in CieLab color)
{
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint))
{
return color;
}
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Adapts <see cref="CieLch"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>.
/// Adapts <see cref="CieLch"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetLabWhitePoint"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public CieLch Adapt(in CieLch color)
{
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint))
{
return color;
}
CieLab labColor = this.ToCieLab(color);
var labColor = this.ToCieLab(color);
return this.ToCieLch(labColor);
}
/// <summary>
/// Adapts <see cref="CieLchuv"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>.
/// Adapts <see cref="CieLchuv"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetLabWhitePoint"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public CieLchuv Adapt(in CieLchuv color)
{
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint))
{
return color;
}
CieLuv luvColor = this.ToCieLuv(color);
var luvColor = this.ToCieLuv(color);
return this.ToCieLchuv(luvColor);
}
/// <summary>
/// Adapts <see cref="CieLuv"/> color from the source white point to white point set in <see cref="TargetLuvWhitePoint"/>.
/// Adapts <see cref="CieLuv"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetLuvWhitePoint"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public CieLuv Adapt(in CieLuv color)
{
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetLuvWhitePoint))
if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLuvWhitePoint))
{
return color;
}
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
/// <summary>
/// Adapts <see cref="HunterLab"/> color from the source white point to white point set in <see cref="TargetHunterLabWhitePoint"/>.
/// Adapts <see cref="HunterLab"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetHunterLabWhitePoint"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public HunterLab Adapt(in HunterLab color)
{
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WhitePoint.Equals(this.TargetHunterLabWhitePoint))
if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetHunterLabWhitePoint))
{
return color;
}
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Adapts a <see cref="LinearRgb"/> color from the source working space to working space set in <see cref="TargetRgbWorkingSpace"/>.
/// Adapts a <see cref="LinearRgb"/> color from the source working space to working space set in <see cref="ColorSpaceConverterOptions.TargetRgbWorkingSpace"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public LinearRgb Adapt(in LinearRgb color)
{
if (!this.IsChromaticAdaptationPerformed)
{
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
}
if (color.WorkingSpace.Equals(this.TargetRgbWorkingSpace))
if (!this.performChromaticAdaptation || color.WorkingSpace.Equals(this.targetRgbWorkingSpace))
{
return color;
}
@ -155,21 +134,25 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = converterToXYZ.Convert(color);
// Adaptation
CieXyz adapted = this.ChromaticAdaptation.Transform(unadapted, color.WorkingSpace.WhitePoint, this.TargetRgbWorkingSpace.WhitePoint);
CieXyz adapted = this.chromaticAdaptation.Transform(unadapted, color.WorkingSpace.WhitePoint, this.targetRgbWorkingSpace.WhitePoint);
// Conversion back to RGB
CieXyzToLinearRgbConverter converterToRGB = this.GetCieXyxToLinearRgbConverter(this.TargetRgbWorkingSpace);
return converterToRGB.Convert(adapted);
return this.cieXyzToLinearRgbConverter.Convert(adapted);
}
/// <summary>
/// Adapts an <see cref="Rgb"/> color from the source working space to working space set in <see cref="TargetRgbWorkingSpace"/>.
/// Adapts an <see cref="Rgb"/> color from the source working space to working space set in <see cref="ColorSpaceConverterOptions.TargetRgbWorkingSpace"/>.
/// </summary>
/// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns>
public Rgb Adapt(in Rgb color)
{
LinearRgb linearInput = this.ToLinearRgb(color);
if (!this.performChromaticAdaptation)
{
return color;
}
var linearInput = this.ToLinearRgb(color);
LinearRgb linearOutput = this.Adapt(linearInput);
return this.ToRgb(linearOutput);
}

324
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs

@ -1,15 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="CieLab"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
/// <summary>
/// The converter for converting between CieLch to CieLab.
@ -26,15 +28,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
// Conversion (perserving white point)
CieLab unadapted = CieLchToCieLabConverter.Convert(color);
if (!this.IsChromaticAdaptationPerformed)
{
return unadapted;
}
// Adaptation
return this.Adapt(unadapted);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="CieLab"/>
/// </summary>
@ -42,10 +60,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in CieLchuv color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="CieLab"/>
/// </summary>
@ -53,10 +93,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in CieLuv color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLab"/>
/// </summary>
@ -64,10 +126,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in CieXyy color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLab"/>
/// </summary>
@ -76,13 +160,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieLab ToCieLab(in CieXyz color)
{
// Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint)
: color;
CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetLabWhitePoint);
// Conversion
var converter = new CieXyzToCieLabConverter(this.TargetLabWhitePoint);
return converter.Convert(adapted);
return this.cieXyzToCieLabConverter.Convert(adapted);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary>
@ -92,10 +194,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Cmyk color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="CieLab"/>
/// </summary>
@ -103,10 +226,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Hsl color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="CieLab"/>
/// </summary>
@ -114,10 +259,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Hsv color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLab"/>
/// </summary>
@ -125,10 +291,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in HunterLab color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="CieLab"/>
/// </summary>
@ -136,10 +324,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Lms color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLab"/>
/// </summary>
@ -147,10 +357,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in LinearRgb color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="CieLab"/>
/// </summary>
@ -158,10 +390,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Rgb color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLab"/>
/// </summary>
@ -169,8 +423,30 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in YCbCr color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="CieLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLab(sp);
}
}
}
}

317
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="CieLch"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
/// <summary>
/// The converter for converting between CieLab to CieLch.
@ -23,12 +26,33 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieLch ToCieLch(in CieLab color)
{
// Adaptation
CieLab adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color;
CieLab adapted = this.Adapt(color);
// Conversion
return CieLabToCieLchConverter.Convert(adapted);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="CieLch"/>
/// </summary>
@ -36,10 +60,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in CieLchuv color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="CieLch"/>
/// </summary>
@ -47,10 +93,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in CieLuv color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLch"/>
/// </summary>
@ -58,10 +126,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in CieXyy color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLch"/>
/// </summary>
@ -69,10 +159,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in CieXyz color)
{
CieLab labColor = this.ToCieLab(color);
var labColor = this.ToCieLab(color);
return this.ToCieLch(labColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="CieLch"/>
/// </summary>
@ -80,10 +192,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in Cmyk color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="CieLch"/>
/// </summary>
@ -91,10 +224,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in Hsl color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="CieLch"/>
/// </summary>
@ -102,10 +257,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in Hsv color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLch"/>
/// </summary>
@ -113,10 +290,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in HunterLab color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLch"/>
/// </summary>
@ -124,10 +323,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in LinearRgb color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="CieLch"/>
/// </summary>
@ -135,10 +356,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in Lms color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="CieLch"/>
/// </summary>
@ -146,10 +389,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in Rgb color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLch"/>
/// </summary>
@ -157,8 +422,30 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in YCbCr color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLch(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="CieLch"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLch> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLch destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLch dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLch(sp);
}
}
}
}

319
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="CieLchuv"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
/// <summary>
/// The converter for converting between CieLab to CieLchuv.
@ -22,10 +25,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in CieLab color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="CieLchuv"/>
/// </summary>
@ -33,10 +58,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in CieLch color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="CieLchuv"/>
/// </summary>
@ -45,12 +92,33 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieLchuv ToCieLchuv(in CieLuv color)
{
// Adaptation
CieLuv adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color;
CieLuv adapted = this.Adapt(color);
// Conversion
return CieLuvToCieLchuvConverter.Convert(adapted);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLchuv"/>
/// </summary>
@ -58,10 +126,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in CieXyy color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLchuv"/>
/// </summary>
@ -69,8 +159,30 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in CieXyz color)
{
CieLab labColor = this.ToCieLab(color);
return this.ToCieLchuv(labColor);
var luvColor = this.ToCieLuv(color);
return this.ToCieLchuv(luvColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary>
@ -80,10 +192,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Cmyk color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="CieLchuv"/>
/// </summary>
@ -91,10 +225,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Hsl color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="CieLchuv"/>
/// </summary>
@ -102,10 +258,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Hsv color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLchuv"/>
/// </summary>
@ -113,10 +291,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in HunterLab color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLchuv"/>
/// </summary>
@ -124,10 +324,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in LinearRgb color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="CieLchuv"/>
/// </summary>
@ -135,10 +357,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Lms color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="CieLchuv"/>
/// </summary>
@ -146,10 +390,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Rgb color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLchuv"/>
/// </summary>
@ -157,8 +423,29 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in YCbCr color)
{
CieXyz xyzColor = this.ToCieXyz(color);
var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="CieLchuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLchuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLchuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLchuv(sp);
}
}
}
}

294
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs

@ -1,16 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="CieLuv"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
private static readonly CieLchuvToCieLuvConverter CieLchuvToCieLuvConverter = new CieLchuvToCieLuvConverter();
@ -25,6 +26,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="CieLuv"/>
/// </summary>
@ -36,6 +58,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="CieLuv"/>
/// </summary>
@ -46,15 +89,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
// Conversion (perserving white point)
CieLuv unadapted = CieLchuvToCieLuvConverter.Convert(color);
if (!this.IsChromaticAdaptationPerformed)
{
return unadapted;
}
// Adaptation
return this.Adapt(unadapted);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLuv"/>
/// </summary>
@ -66,6 +125,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLuv"/>
/// </summary>
@ -74,13 +154,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieLuv ToCieLuv(in CieXyz color)
{
// Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint)
: color;
CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetLuvWhitePoint);
// Conversion
var converter = new CieXyzToCieLuvConverter(this.TargetLuvWhitePoint);
return converter.Convert(adapted);
return this.cieXyzToCieLuvConverter.Convert(adapted);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary>
@ -94,6 +192,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="CieLuv"/>
/// </summary>
@ -105,6 +224,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="CieLuv"/>
/// </summary>
@ -116,6 +256,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLuv"/>
/// </summary>
@ -127,6 +288,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="CieLuv"/>
/// </summary>
@ -138,6 +320,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLuv"/>
/// </summary>
@ -149,6 +352,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="CieLuv"/>
/// </summary>
@ -160,6 +384,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieLuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLuv"/>
/// </summary>
@ -170,5 +415,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
var xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="CieLuv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieLuv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieLuv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref CieLuv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieLuv(sp);
}
}
}
}

281
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="CieXyy"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
private static readonly CieXyzAndCieXyyConverter CieXyzAndCieXyyConverter = new CieXyzAndCieXyyConverter();
@ -24,6 +27,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="CieXyy"/>
/// </summary>
@ -36,6 +60,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="CieXyy"/>
/// </summary>
@ -48,6 +93,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="CieXyy"/>
/// </summary>
@ -60,14 +126,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="CieXyy"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in CieXyz color)
public CieXyy ToCieXyy(in CieXyz color) => CieXyzAndCieXyyConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieXyy> destination, int count)
{
return CieXyzAndCieXyyConverter.Convert(color);
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary>
@ -82,6 +187,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="CieXyy"/>
/// </summary>
@ -94,6 +220,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="CieXyy"/>
/// </summary>
@ -106,6 +253,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieXyy"/>
/// </summary>
@ -118,6 +286,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieXyy"/>
/// </summary>
@ -130,6 +319,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="CieXyy"/>
/// </summary>
@ -142,6 +352,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="CieXyy"/>
/// </summary>
@ -154,6 +385,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="CieXyy"/>
/// </summary>
@ -165,5 +417,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyy(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieXyy> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyy destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
}
}
}
}

305
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs

@ -1,17 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="CieXyz"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
private static readonly CieLabToCieXyzConverter CieLabToCieXyzConverter = new CieLabToCieXyzConverter();
@ -32,11 +32,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = CieLabToCieXyzConverter.Convert(color);
// Adaptation
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed
? unadapted
: this.Adapt(unadapted, color.WhitePoint);
return this.Adapt(unadapted, color.WhitePoint);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
return adapted;
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
@ -53,6 +70,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(labColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="CieXyz"/>
/// </summary>
@ -67,6 +105,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(luvColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="CieXyz"/>
/// </summary>
@ -78,11 +137,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = CieLuvToCieXyzConverter.Convert(color);
// Adaptation
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed
? unadapted
: this.Adapt(unadapted, color.WhitePoint);
return this.Adapt(unadapted, color.WhitePoint);
}
return adapted;
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
@ -96,6 +172,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CieXyzAndCieXyyConverter.Convert(color);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="CieXyz"/>
/// </summary>
@ -109,6 +206,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="CieXyz"/>
/// </summary>
@ -122,6 +240,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="CieXyz"/>
/// </summary>
@ -135,6 +274,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="CieXyz"/>
/// </summary>
@ -146,11 +306,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color);
// Adaptation
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed
? unadapted
: this.Adapt(unadapted, color.WhitePoint);
return this.Adapt(unadapted, color.WhitePoint);
}
return adapted;
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
@ -165,9 +342,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = converter.Convert(color);
// Adaptation
return color.WorkingSpace.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed
? unadapted
: this.Adapt(unadapted, color.WorkingSpace.WhitePoint);
return this.Adapt(unadapted, color.WorkingSpace.WhitePoint);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
@ -178,7 +374,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieXyz ToCieXyz(in Lms color)
{
// Conversion
return this.cachedCieXyzAndLmsConverter.Convert(color);
return this.cieXyzAndLmsConverter.Convert(color);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
@ -193,6 +410,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(linear);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="CieXyz"/>
/// </summary>
@ -206,14 +444,35 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCieXyz(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="CieXyz"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<CieXyz> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
}
}
/// <summary>
/// Gets the correct converter for the given rgb working space.
/// </summary>
/// <param name="workingSpace">The source working space</param>
/// <returns>The <see cref="LinearRgbToCieXyzConverter"/></returns>
private LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace)
private LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpaceBase workingSpace)
{
if (this.linearRgbToCieXyzConverter != null && this.linearRgbToCieXyzConverter.SourceWorkingSpace.Equals(workingSpace))
if (this.linearRgbToCieXyzConverter?.SourceWorkingSpace.Equals(workingSpace) == true)
{
return this.linearRgbToCieXyzConverter;
}

281
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CmykColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="Cmyk"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
private static readonly CmykAndRgbConverter CmykAndRgbConverter = new CmykAndRgbConverter();
@ -24,6 +27,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Cmyk"/>
/// </summary>
@ -36,6 +60,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Cmyk"/>
/// </summary>
@ -48,6 +93,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Cmyk"/>
/// </summary>
@ -60,6 +126,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Cmyk"/>
/// </summary>
@ -72,6 +159,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="Cmyk"/>
/// </summary>
@ -84,6 +192,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Cmyk"/>
/// </summary>
@ -96,6 +225,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Cmyk"/>
/// </summary>
@ -108,6 +258,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Cmyk"/>
/// </summary>
@ -120,6 +291,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Cmyk"/>
/// </summary>
@ -132,6 +324,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="Cmyk"/>
/// </summary>
@ -144,14 +357,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToCmyk(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="Cmyk"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in Rgb color)
public Cmyk ToCmyk(in Rgb color) => CmykAndRgbConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<Cmyk> destination, int count)
{
return CmykAndRgbConverter.Convert(color);
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
/// <summary>
@ -165,5 +417,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Cmyk> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Cmyk destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
}
}
}
}

281
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="Hsl"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
private static readonly HslAndRgbConverter HslAndRgbConverter = new HslAndRgbConverter();
@ -24,6 +27,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Hsl"/>
/// </summary>
@ -36,6 +60,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Hsl"/>
/// </summary>
@ -48,6 +93,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Hsl"/>
/// </summary>
@ -60,6 +126,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Hsl"/>
/// </summary>
@ -72,6 +159,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="Hsl"/>
/// </summary>
@ -84,6 +192,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="Hsl"/>
/// </summary>
@ -96,6 +225,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Hsl"/>
/// </summary>
@ -108,6 +258,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Hsl"/>
/// </summary>
@ -120,6 +291,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Hsl"/>
/// </summary>
@ -132,6 +324,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="Hsl"/>
/// </summary>
@ -144,14 +357,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsl(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="Hsl"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in Rgb color)
public Hsl ToHsl(in Rgb color) => HslAndRgbConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<Hsl> destination, int count)
{
return HslAndRgbConverter.Convert(color);
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
/// <summary>
@ -165,5 +417,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Hsl"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Hsl> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsl destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
}
}
}
}

281
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="Hsv"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
private static readonly HsvAndRgbConverter HsvAndRgbConverter = new HsvAndRgbConverter();
@ -24,6 +27,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Hsv"/>
/// </summary>
@ -36,6 +60,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Hsv"/>
/// </summary>
@ -48,6 +93,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Hsv"/>
/// </summary>
@ -60,6 +126,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Hsv"/>
/// </summary>
@ -72,6 +159,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="Hsv"/>
/// </summary>
@ -84,6 +192,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HsvAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="Hsv"/>
/// </summary>
@ -96,6 +225,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HsvAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Hsv"/>
/// </summary>
@ -108,6 +258,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HsvAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Hsv"/>
/// </summary>
@ -120,6 +291,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Hsv"/>
/// </summary>
@ -132,6 +324,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HsvAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="Hsv"/>
/// </summary>
@ -144,14 +357,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHsv(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="Hsv"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in Rgb color)
public Hsv ToHsv(in Rgb color) => HsvAndRgbConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<Hsv> destination, int count)
{
return HsvAndRgbConverter.Convert(color);
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
/// <summary>
@ -165,5 +417,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HsvAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Hsv> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Hsv destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
}
}
}
}

285
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs

@ -1,14 +1,16 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="HunterLab"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="HunterLab"/>
@ -21,6 +23,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="HunterLab"/>
/// </summary>
@ -32,6 +55,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="HunterLab"/>
/// </summary>
@ -43,6 +87,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="HunterLab"/>
/// </summary>
@ -54,6 +119,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="HunterLab"/>
/// </summary>
@ -65,6 +151,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="HunterLab"/>
/// </summary>
@ -73,12 +180,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public HunterLab ToHunterLab(in CieXyz color)
{
// Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetHunterLabWhitePoint) && this.IsChromaticAdaptationPerformed
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetHunterLabWhitePoint)
: color;
CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetHunterLabWhitePoint);
// Conversion
return new CieXyzToHunterLabConverter(this.TargetHunterLabWhitePoint).Convert(adapted);
return this.cieXyzToHunterLabConverter.Convert(adapted);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
@ -92,6 +218,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="HunterLab"/>
/// </summary>
@ -103,6 +250,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="HunterLab"/>
/// </summary>
@ -114,6 +282,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="HunterLab"/>
/// </summary>
@ -125,6 +314,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="HunterLab"/>
/// </summary>
@ -136,6 +346,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="HunterLab"/>
/// </summary>
@ -147,6 +378,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="HunterLab"/>
/// </summary>
@ -157,5 +409,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
var xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="HunterLab"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<HunterLab> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref HunterLab destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref HunterLab dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHunterLab(sp);
}
}
}
}

290
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs

@ -1,19 +1,20 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="LinearRgb"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
private static readonly RgbToLinearRgbConverter RgbToLinearRgbConverter = new RgbToLinearRgbConverter();
private CieXyzToLinearRgbConverter cieXyzToLinearRgbConverter;
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="LinearRgb"/>
/// </summary>
@ -25,6 +26,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="LinearRgb"/>
/// </summary>
@ -36,6 +58,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="LinearRgb"/>
/// </summary>
@ -47,6 +90,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="LinearRgb"/>
/// </summary>
@ -58,6 +122,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="LinearRgb"/>
/// </summary>
@ -69,6 +154,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="LinearRgb"/>
/// </summary>
@ -77,13 +183,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public LinearRgb ToLinearRgb(in CieXyz color)
{
// Adaptation
CieXyz adapted = this.TargetRgbWorkingSpace.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed
? color
: this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetRgbWorkingSpace.WhitePoint);
CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetRgbWorkingSpace.WhitePoint);
// Conversion
CieXyzToLinearRgbConverter xyzConverter = this.GetCieXyxToLinearRgbConverter(this.TargetRgbWorkingSpace);
return xyzConverter.Convert(adapted);
return this.cieXyzToLinearRgbConverter.Convert(adapted);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary>
@ -97,6 +221,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="LinearRgb"/>
/// </summary>
@ -108,6 +253,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="LinearRgb"/>
/// </summary>
@ -119,6 +285,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="LinearRgb"/>
/// </summary>
@ -130,6 +317,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="LinearRgb"/>
/// </summary>
@ -141,6 +349,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLinearRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="LinearRgb"/>
/// </summary>
@ -152,6 +381,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return RgbToLinearRgbConverter.Convert(color);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<LinearRgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="LinearRgb"/>
/// </summary>
@ -164,18 +414,24 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
/// <summary>
/// Gets the correct converter for the given rgb working space.
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="LinearRgb"/>
/// </summary>
/// <param name="workingSpace">The target working space</param>
/// <returns>The <see cref="CieXyzToLinearRgbConverter"/></returns>
private CieXyzToLinearRgbConverter GetCieXyxToLinearRgbConverter(RgbWorkingSpace workingSpace)
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<LinearRgb> destination, int count)
{
if (this.cieXyzToLinearRgbConverter != null && this.cieXyzToLinearRgbConverter.TargetWorkingSpace.Equals(workingSpace))
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
return this.cieXyzToLinearRgbConverter;
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
}
return this.cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(workingSpace);
}
}
}

280
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs

@ -1,14 +1,16 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="Lms"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Lms"/>
@ -21,6 +23,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Lms"/>
/// </summary>
@ -32,6 +55,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Lms"/>
/// </summary>
@ -43,6 +87,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Lms"/>
/// </summary>
@ -54,6 +119,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Lms"/>
/// </summary>
@ -65,14 +151,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="Lms"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieXyz color)
public Lms ToLms(in CieXyz color) => this.cieXyzAndLmsConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Lms> destination, int count)
{
return this.cachedCieXyzAndLmsConverter.Convert(color);
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary>
@ -86,6 +211,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Lms"/>
/// </summary>
@ -97,6 +243,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Lms"/>
/// </summary>
@ -108,6 +275,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Lms"/>
/// </summary>
@ -119,6 +307,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Lms"/>
/// </summary>
@ -130,6 +339,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="Lms"/>
/// </summary>
@ -141,6 +371,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToLms(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="Lms"/>
/// </summary>
@ -151,5 +402,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
var xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Lms"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Lms> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Lms destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref Lms dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLms(sp);
}
}
}
}

280
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="Rgb"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
private static readonly LinearRgbToRgbConverter LinearRgbToRgbConverter = new LinearRgbToRgbConverter();
@ -23,6 +26,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="Rgb"/>
/// </summary>
@ -34,6 +58,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="Rgb"/>
/// </summary>
@ -45,6 +90,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLchuv"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLchuv> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLuv"/> into a <see cref="Rgb"/>
/// </summary>
@ -56,6 +122,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="Rgb"/>
/// </summary>
@ -67,6 +154,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="Rgb"/>
/// </summary>
@ -81,6 +189,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(linear);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="Rgb"/>
/// </summary>
@ -92,6 +221,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return CmykAndRgbConverter.Convert(color);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Rgb"/>
/// </summary>
@ -103,6 +253,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HsvAndRgbConverter.Convert(color);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Rgb"/>
/// </summary>
@ -114,6 +285,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return HslAndRgbConverter.Convert(color);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Rgb"/>
/// </summary>
@ -125,6 +317,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="Rgb"/>
/// </summary>
@ -136,6 +349,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return LinearRgbToRgbConverter.Convert(color);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="Rgb"/>
/// </summary>
@ -147,6 +381,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToRgb(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="Rgb"/>
/// </summary>
@ -160,5 +415,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
// Adaptation
return this.Adapt(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="YCbCr"/> into <see cref="Rgb"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<YCbCr> source, Span<Rgb> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source);
ref Rgb destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
}
}
}
}

260
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs

@ -1,14 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <content>
/// Allows conversion to <see cref="YCbCr"/>.
/// </content>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter();
@ -24,6 +27,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToYCbCr(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLab> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLab sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLab sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="YCbCr"/>
/// </summary>
@ -37,15 +61,24 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
/// <summary>
/// Converts a <see cref="CieLchuv"/> into a <see cref="YCbCr"/>
/// Performs the bulk conversion from <see cref="CieLch"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLchuv color)
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLch> source, Span<YCbCr> destination, int count)
{
var xyzColor = this.ToCieXyz(color);
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
return this.ToYCbCr(xyzColor);
ref CieLch sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLch sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary>
@ -60,6 +93,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToYCbCr(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieLuv"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieLuv> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyy"/> into a <see cref="YCbCr"/>
/// </summary>
@ -72,6 +126,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToYCbCr(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary>
/// Converts a <see cref="CieXyz"/> into a <see cref="YCbCr"/>
/// </summary>
@ -84,6 +159,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary>
/// Converts a <see cref="Cmyk"/> into a <see cref="YCbCr"/>
/// </summary>
@ -96,6 +192,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Cmyk"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="YCbCr"/>
/// </summary>
@ -108,6 +225,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsl"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsl sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="YCbCr"/>
/// </summary>
@ -120,6 +258,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Hsv"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Hsv sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="YCbCr"/>
/// </summary>
@ -132,6 +291,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToYCbCr(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="HunterLab"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<HunterLab> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary>
/// Converts a <see cref="LinearRgb"/> into a <see cref="YCbCr"/>
/// </summary>
@ -144,6 +324,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return YCbCrAndRgbConverter.Convert(rgb);
}
/// <summary>
/// Performs the bulk conversion from <see cref="LinearRgb"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="YCbCr"/>
/// </summary>
@ -156,14 +357,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return this.ToYCbCr(xyzColor);
}
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Lms> source, Span<YCbCr> destination, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Lms sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Lms sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
/// <summary>
/// Converts a <see cref="Rgb"/> into a <see cref="YCbCr"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Rgb color)
public YCbCr ToYCbCr(in Rgb color) => YCbCrAndRgbConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="YCbCr"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
/// <param name="count">The number of colors to convert.</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<YCbCr> destination, int count)
{
return YCbCrAndRgbConverter.Convert(color);
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
ref Rgb sourceRef = ref MemoryMarshal.GetReference(source);
ref YCbCr destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
}
}
}
}

114
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs

@ -2,100 +2,60 @@
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <summary>
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
/// Provides methods to allow the conversion of color values between different color spaces.
/// </summary>
internal partial class ColorSpaceConverter
public partial class ColorSpaceConverter
{
/// <summary>
/// The default whitepoint used for converting to CieLab
/// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65;
private Matrix4x4 transformationMatrix;
private CieXyzAndLmsConverter cachedCieXyzAndLmsConverter;
// Options.
private static readonly ColorSpaceConverterOptions DefaultOptions = new ColorSpaceConverterOptions();
private readonly Matrix4x4 lmsAdaptationMatrix;
private readonly CieXyz whitePoint;
private readonly CieXyz targetLuvWhitePoint;
private readonly CieXyz targetLabWhitePoint;
private readonly CieXyz targetHunterLabWhitePoint;
private readonly RgbWorkingSpaceBase targetRgbWorkingSpace;
private readonly IChromaticAdaptation chromaticAdaptation;
private readonly bool performChromaticAdaptation;
private readonly CieXyzAndLmsConverter cieXyzAndLmsConverter;
private readonly CieXyzToCieLabConverter cieXyzToCieLabConverter;
private readonly CieXyzToCieLuvConverter cieXyzToCieLuvConverter;
private readonly CieXyzToHunterLabConverter cieXyzToHunterLabConverter;
private readonly CieXyzToLinearRgbConverter cieXyzToLinearRgbConverter;
/// <summary>
/// Initializes a new instance of the <see cref="ColorSpaceConverter"/> class.
/// </summary>
public ColorSpaceConverter()
: this(DefaultOptions)
{
// Note the order here this is important.
this.WhitePoint = DefaultWhitePoint;
this.LmsAdaptationMatrix = CieXyzAndLmsConverter.DefaultTransformationMatrix;
this.ChromaticAdaptation = new VonKriesChromaticAdaptation(this.cachedCieXyzAndLmsConverter);
this.TargetLuvWhitePoint = CieLuv.DefaultWhitePoint;
this.TargetLabWhitePoint = CieLab.DefaultWhitePoint;
this.TargetHunterLabWhitePoint = HunterLab.DefaultWhitePoint;
this.TargetRgbWorkingSpace = Rgb.DefaultWorkingSpace;
}
/// <summary>
/// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space.
/// When null, no adaptation will be performed.
/// </summary>
public CieXyz WhitePoint { get; set; }
/// <summary>
/// Gets or sets the white point used *when creating* Luv/LChuv colors. (Luv/LChuv colors on the input already contain the white point information)
/// Defaults to: <see cref="CieLuv.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetLuvWhitePoint { get; set; }
/// <summary>
/// Gets or sets the white point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information)
/// Defaults to: <see cref="CieLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetLabWhitePoint { get; set; }
/// <summary>
/// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information)
/// Defaults to: <see cref="HunterLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetHunterLabWhitePoint { get; set; }
/// <summary>
/// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information)
/// Defaults to: <see cref="Rgb.DefaultWorkingSpace"/>.
/// </summary>
public RgbWorkingSpace TargetRgbWorkingSpace { get; set; }
/// <summary>
/// Gets or sets the chromatic adaptation method used. When null, no adaptation will be performed.
/// </summary>
public IChromaticAdaptation ChromaticAdaptation { get; set; }
/// <summary>
/// Gets or sets transformation matrix used in conversion to <see cref="Lms"/>,
/// also used in the default Von Kries Chromatic Adaptation method.
/// Initializes a new instance of the <see cref="ColorSpaceConverter"/> class.
/// </summary>
public Matrix4x4 LmsAdaptationMatrix
/// <param name="options">The configuration options.</param>
public ColorSpaceConverter(ColorSpaceConverterOptions options)
{
get => this.transformationMatrix;
set
{
this.transformationMatrix = value;
if (this.cachedCieXyzAndLmsConverter == null)
{
this.cachedCieXyzAndLmsConverter = new CieXyzAndLmsConverter(value);
}
else
{
this.cachedCieXyzAndLmsConverter.TransformationMatrix = value;
}
}
Guard.NotNull(options, nameof(options));
this.whitePoint = options.WhitePoint;
this.targetLuvWhitePoint = options.TargetLuvWhitePoint;
this.targetLabWhitePoint = options.TargetLabWhitePoint;
this.targetHunterLabWhitePoint = options.TargetHunterLabWhitePoint;
this.targetRgbWorkingSpace = options.TargetRgbWorkingSpace;
this.chromaticAdaptation = options.ChromaticAdaptation;
this.performChromaticAdaptation = this.chromaticAdaptation != null;
this.lmsAdaptationMatrix = options.LmsAdaptationMatrix;
this.cieXyzAndLmsConverter = new CieXyzAndLmsConverter(this.lmsAdaptationMatrix);
this.cieXyzToCieLabConverter = new CieXyzToCieLabConverter(this.targetLabWhitePoint);
this.cieXyzToCieLuvConverter = new CieXyzToCieLuvConverter(this.targetLuvWhitePoint);
this.cieXyzToHunterLabConverter = new CieXyzToHunterLabConverter(this.targetHunterLabWhitePoint);
this.cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(this.targetRgbWorkingSpace);
}
/// <summary>
/// Gets a value indicating whether chromatic adaptation has been performed.
/// </summary>
private bool IsChromaticAdaptationPerformed => this.ChromaticAdaptation != null;
}
}

55
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs

@ -0,0 +1,55 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <summary>
/// Configuration options for the <see cref="ColorSpaceConverter"/> class.
/// </summary>
public class ColorSpaceConverterOptions
{
/// <summary>
/// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space.
/// When <value>default</value>, no adaptation will be performed.
/// Defaults to: <see cref="CieLuv.DefaultWhitePoint"/>.
/// </summary>
public CieXyz WhitePoint { get; set; } = CieLuv.DefaultWhitePoint;
/// <summary>
/// Gets or sets the white point used *when creating* Luv/LChuv colors. (Luv/LChuv colors on the input already contain the white point information)
/// Defaults to: <see cref="CieLuv.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetLuvWhitePoint { get; set; } = CieLuv.DefaultWhitePoint;
/// <summary>
/// Gets or sets the white point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information)
/// Defaults to: <see cref="CieLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetLabWhitePoint { get; set; } = CieLab.DefaultWhitePoint;
/// <summary>
/// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information)
/// Defaults to: <see cref="HunterLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetHunterLabWhitePoint { get; set; } = HunterLab.DefaultWhitePoint;
/// <summary>
/// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information)
/// Defaults to: <see cref="Rgb.DefaultWorkingSpace"/>.
/// </summary>
public RgbWorkingSpaceBase TargetRgbWorkingSpace { get; set; } = Rgb.DefaultWorkingSpace;
/// <summary>
/// Gets or sets the chromatic adaptation method used. When <value>null</value>, no adaptation will be performed.
/// </summary>
public IChromaticAdaptation ChromaticAdaptation { get; set; } = new VonKriesChromaticAdaptation();
/// <summary>
/// Gets or sets transformation matrix used in conversion to and from <see cref="Lms"/>.
/// </summary>
public Matrix4x4 LmsAdaptationMatrix { get; set; } = CieXyzAndLmsConverter.DefaultTransformationMatrix;
}
}

21
src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <summary>
@ -8,16 +10,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// A linear transformation of a source color (XS, YS, ZS) into a destination color (XD, YD, ZD) by a linear transformation [M]
/// which is dependent on the source reference white (XWS, YWS, ZWS) and the destination reference white (XWD, YWD, ZWD).
/// </summary>
internal interface IChromaticAdaptation
public interface IChromaticAdaptation
{
/// <summary>
/// Performs a linear transformation of a source color in to the destination color.
/// </summary>
/// <remarks>Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates).</remarks>
/// <param name="sourceColor">The source color.</param>
/// <param name="source">The source color.</param>
/// <param name="sourceWhitePoint">The source white point.</param>
/// <param name="targetWhitePoint">The target white point.</param>
/// <param name="destinationWhitePoint">The destination white point.</param>
/// <returns>The <see cref="CieXyz"/></returns>
CieXyz Transform(in CieXyz sourceColor, in CieXyz sourceWhitePoint, in CieXyz targetWhitePoint);
CieXyz Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint);
/// <summary>
/// Performs a bulk linear transformation of a source color in to the destination color.
/// </summary>
/// <remarks>Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates).</remarks>
/// <param name="source">The span to the source colors.</param>
/// <param name="destination">The span to the destination colors.</param>
/// <param name="sourceWhitePoint">The source white point.</param>
/// <param name="destinationWhitePoint">The destination white point.</param>
/// <param name="count">The number of colors to convert.</param>
void Transform(Span<CieXyz> source, Span<CieXyz> destination, CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint, int count);
}
}

22
src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs

@ -1,22 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <summary>
/// Converts color between two color spaces.
/// </summary>
/// <typeparam name="T">The input color type.</typeparam>
/// <typeparam name="TResult">The result color type.</typeparam>
internal interface IColorConversion<T, TResult>
where T : struct
where TResult : struct
{
/// <summary>
/// Performs the conversion from the input to an instance of the output type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
TResult Convert(in T input);
}
}

49
src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs

@ -1,49 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CmykColorSapce
{
/// <summary>
/// Color converter between CMYK and Rgb
/// </summary>
internal class CmykAndRgbConverter : IColorConversion<Cmyk, Rgb>, IColorConversion<Rgb, Cmyk>
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb Convert(in Cmyk input)
{
float r = (1F - input.C) * (1F - input.K);
float g = (1F - input.M) * (1F - input.K);
float b = (1F - input.Y) * (1F - input.K);
return new Rgb(r, g, b);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cmyk Convert(in Rgb input)
{
// To CMYK
float c = 1F - input.R;
float m = 1F - input.G;
float y = 1F - input.B;
// To CMYK
float k = MathF.Min(c, MathF.Min(m, y));
if (MathF.Abs(k - 1F) < Constants.Epsilon)
{
return new Cmyk(0, 0, 0, 1F);
}
c = (c - k) / (1F - k);
m = (m - k) / (1F - k);
y = (y - k) / (1F - k);
return new Cmyk(c, m, y, k);
}
}
}

12
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs

@ -4,15 +4,19 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>.
/// </summary>
internal class CieLchToCieLabConverter : IColorConversion<CieLch, CieLab>
internal sealed class CieLchToCieLabConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="CieLch"/> input to an instance of <see cref="CieLab"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLab Convert(in CieLch input)
{
// Conversion algorithm described here:

14
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs

@ -4,15 +4,19 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>.
/// </summary>
internal class CieLabToCieLchConverter : IColorConversion<CieLab, CieLch>
internal sealed class CieLabToCieLchConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="CieLab"/> input to an instance of <see cref="CieLch"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLch Convert(in CieLab input)
{
// Conversion algorithm described here:
@ -23,7 +27,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColor
float hDegrees = MathFExtensions.RadianToDegree(hRadians);
// Wrap the angle round at 360.
hDegrees = hDegrees % 360;
hDegrees %= 360;
// Make sure it's not negative.
while (hDegrees < 0)

33
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs

@ -1,18 +1,22 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieXyz"/>.
/// </summary>
internal class CieLabToCieXyzConverter : IColorConversion<CieLab, CieXyz>
internal sealed class CieLabToCieXyzConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="CieLab"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in CieLab input)
{
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
@ -21,25 +25,20 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor
float fx = (a / 500F) + fy;
float fz = fy - (b / 200F);
float fx3 = MathF.Pow(fx, 3F);
float fz3 = MathF.Pow(fz, 3F);
float fx3 = ImageMaths.Pow3(fx);
float fz3 = ImageMaths.Pow3(fz);
float xr = fx3 > CieConstants.Epsilon ? fx3 : ((116F * fx) - 16F) / CieConstants.Kappa;
float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? MathF.Pow((l + 16F) / 116F, 3F) : l / CieConstants.Kappa;
float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? ImageMaths.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa;
float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa;
float wx = input.WhitePoint.X, wy = input.WhitePoint.Y, wz = input.WhitePoint.Z;
var wxyz = new Vector3(input.WhitePoint.X, input.WhitePoint.Y, input.WhitePoint.Z);
// Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white)
xr = xr.Clamp(0, 1F);
yr = yr.Clamp(0, 1F);
zr = zr.Clamp(0, 1F);
var xyzr = Vector3.Clamp(new Vector3(xr, yr, zr), Vector3.Zero, Vector3.One);
float x = xr * wx;
float y = yr * wy;
float z = zr * wz;
return new CieXyz(x, y, z);
Vector3 xyz = xyzr * wxyz;
return new CieXyz(xyz);
}
}
}

12
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs

@ -4,15 +4,19 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>.
/// </summary>
internal class CieLchuvToCieLuvConverter : IColorConversion<CieLchuv, CieLuv>
internal sealed class CieLchuvToCieLuvConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="CieLchuv"/> input to an instance of <see cref="CieLuv"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLuv Convert(in CieLchuv input)
{
// Conversion algorithm described here:

14
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs

@ -4,15 +4,19 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>.
/// </summary>
internal class CieLuvToCieLchuvConverter : IColorConversion<CieLuv, CieLchuv>
internal sealed class CieLuvToCieLchuvConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="CieLuv"/> input to an instance of <see cref="CieLchuv"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv Convert(in CieLuv input)
{
// Conversion algorithm described here:
@ -23,7 +27,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvCol
float hDegrees = MathFExtensions.RadianToDegree(hRadians);
// Wrap the angle round at 360.
hDegrees = hDegrees % 360;
hDegrees %= 360;
// Make sure it's not negative.
while (hDegrees < 0)

28
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs

@ -1,18 +1,20 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieLuv"/> to <see cref="CieXyz"/>.
/// </summary>
internal class CieLuvToCieXyzConverter : IColorConversion<CieLuv, CieXyz>
internal sealed class CieLuvToCieXyzConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="CieLuv"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
public CieXyz Convert(in CieLuv input)
{
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html
@ -22,12 +24,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
float v0 = ComputeV0(input.WhitePoint);
float y = l > CieConstants.Kappa * CieConstants.Epsilon
? MathF.Pow((l + 16) / 116, 3)
? ImageMaths.Pow3((l + 16) / 116)
: l / CieConstants.Kappa;
float a = ((52 * l / (u + (13 * l * u0))) - 1) / 3;
float b = -5 * y;
float c = -0.3333333F;
const float c = -0.3333333F;
float d = y * ((39 * l / (v + (13 * l * v0))) - 5);
float x = (d - b) / (a - c);
@ -56,21 +58,17 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
/// </summary>
/// <param name="input">The whitepoint</param>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static float ComputeU0(in CieXyz input)
{
return (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z));
}
=> (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z));
/// <summary>
/// Calculates the red-green chromacity based on the given whitepoint.
/// </summary>
/// <param name="input">The whitepoint</param>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static float ComputeV0(in CieXyz input)
{
return (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
}
=> (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
}
}

20
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs

@ -4,16 +4,20 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between CIE XYZ and CIE xyY
/// <see href="http://www.brucelindbloom.com/"/> for formulas.
/// </summary>
internal class CieXyzAndCieXyyConverter : IColorConversion<CieXyz, CieXyy>, IColorConversion<CieXyy, CieXyz>
internal sealed class CieXyzAndCieXyyConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="CieXyy"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyy Convert(in CieXyz input)
{
float x = input.X / (input.X + input.Y + input.Z);
@ -27,8 +31,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColor
return new CieXyy(x, y, input.Y);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="CieXyy"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in CieXyy input)
{
if (MathF.Abs(input.Y) < Constants.Epsilon)

6
src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs

@ -3,7 +3,7 @@
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// The base class for converting between <see cref="HunterLab"/> and <see cref="CieXyz"/> color spaces.
@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
/// </summary>
/// <param name="whitePoint">The whitepoint</param>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static float ComputeKa(CieXyz whitePoint)
{
if (whitePoint.Equals(Illuminants.C))
@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
/// </summary>
/// <param name="whitePoint">The whitepoint</param>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static float ComputeKb(CieXyz whitePoint)
{
if (whitePoint == Illuminants.C)

45
src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs

@ -4,12 +4,12 @@
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between CIE XYZ and LMS
/// Color converter between <see cref="CieXyz"/> and <see cref="Lms"/>
/// </summary>
internal class CieXyzAndLmsConverter : IColorConversion<CieXyz, Lms>, IColorConversion<Lms, CieXyz>
internal sealed class CieXyzAndLmsConverter
{
/// <summary>
/// Default transformation matrix used, when no other is set. (Bradford)
@ -23,7 +23,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
/// <summary>
/// Initializes a new instance of the <see cref="CieXyzAndLmsConverter"/> class.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzAndLmsConverter()
: this(DefaultTransformationMatrix)
{
@ -36,41 +35,35 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
/// Definition of the cone response domain (see <see cref="LmsAdaptationMatrix"/>),
/// if not set <see cref="DefaultTransformationMatrix"/> will be used.
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix)
{
this.TransformationMatrix = transformationMatrix;
this.transformationMatrix = transformationMatrix;
Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix);
}
/// <summary>
/// Gets or sets the transformation matrix used for the conversion (definition of the cone response domain).
/// <see cref="LmsAdaptationMatrix"/>
/// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="Lms"/> type.
/// </summary>
public Matrix4x4 TransformationMatrix
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.transformationMatrix;
set
{
this.transformationMatrix = value;
Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix);
}
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Lms Convert(in CieXyz input)
{
Vector3 vector = Vector3.Transform(input.Vector, this.transformationMatrix);
var vector = Vector3.Transform(input.ToVector3(), this.transformationMatrix);
return new Lms(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="Lms"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in Lms input)
{
Vector3 vector = Vector3.Transform(input.Vector, this.inverseTransformationMatrix);
var vector = Vector3.Transform(input.ToVector3(), this.inverseTransformationMatrix);
return new CieXyz(vector);
}
}

19
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs

@ -4,17 +4,16 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieXyz"/> to <see cref="CieLab"/>.
/// </summary>
internal class CieXyzToCieLabConverter : IColorConversion<CieXyz, CieLab>
internal sealed class CieXyzToCieLabConverter
{
/// <summary>
/// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzToCieLabConverter()
: this(CieLab.DefaultWhitePoint)
{
@ -24,19 +23,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor
/// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class.
/// </summary>
/// <param name="labWhitePoint">The target reference lab white point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzToCieLabConverter(CieXyz labWhitePoint)
{
this.LabWhitePoint = labWhitePoint;
}
public CieXyzToCieLabConverter(CieXyz labWhitePoint) => this.LabWhitePoint = labWhitePoint;
/// <summary>
/// Gets the target reference whitepoint. When not set, <see cref="CieLab.DefaultWhitePoint"/> is used.
/// </summary>
public CieXyz LabWhitePoint { get; }
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="CieLab"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLab Convert(in CieXyz input)
{
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html

28
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs

@ -3,19 +3,17 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Converts from <see cref="CieXyz"/> to <see cref="CieLuv"/>.
/// </summary>
internal class CieXyzToCieLuvConverter : IColorConversion<CieXyz, CieLuv>
internal sealed class CieXyzToCieLuvConverter
{
/// <summary>
/// Initializes a new instance of the <see cref="CieXyzToCieLuvConverter"/> class.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzToCieLuvConverter()
: this(CieLuv.DefaultWhitePoint)
{
@ -25,19 +23,18 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
/// Initializes a new instance of the <see cref="CieXyzToCieLuvConverter"/> class.
/// </summary>
/// <param name="luvWhitePoint">The target reference luv white point</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzToCieLuvConverter(CieXyz luvWhitePoint)
{
this.LuvWhitePoint = luvWhitePoint;
}
public CieXyzToCieLuvConverter(CieXyz luvWhitePoint) => this.LuvWhitePoint = luvWhitePoint;
/// <summary>
/// Gets the target reference whitepoint. When not set, <see cref="CieLuv.DefaultWhitePoint"/> is used.
/// </summary>
public CieXyz LuvWhitePoint { get; }
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="CieLuv"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
public CieLuv Convert(in CieXyz input)
{
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html
@ -77,18 +74,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
/// <returns>The <see cref="float"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float ComputeUp(in CieXyz input)
{
return (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z));
}
=> (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z));
/// <summary>
/// Calculates the red-green chromacity based on the given whitepoint.
/// </summary>
/// <param name="input">The whitepoint</param>
/// <returns>The <see cref="float"/></returns>
[MethodImpl(InliningOptions.ShortMethod)]
private static float ComputeVp(in CieXyz input)
{
return (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
}
=> (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
}
}

21
src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs

@ -4,17 +4,16 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between CieXyz and HunterLab
/// Color converter between <see cref="CieXyz"/> and <see cref="HunterLab"/>
/// </summary>
internal class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase, IColorConversion<CieXyz, HunterLab>
internal sealed class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase
{
/// <summary>
/// Initializes a new instance of the <see cref="CieXyzToHunterLabConverter"/> class.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzToHunterLabConverter()
: this(HunterLab.DefaultWhitePoint)
{
@ -24,19 +23,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
/// Initializes a new instance of the <see cref="CieXyzToHunterLabConverter"/> class.
/// </summary>
/// <param name="labWhitePoint">The hunter Lab white point.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzToHunterLabConverter(CieXyz labWhitePoint)
{
this.HunterLabWhitePoint = labWhitePoint;
}
public CieXyzToHunterLabConverter(CieXyz labWhitePoint) => this.HunterLabWhitePoint = labWhitePoint;
/// <summary>
/// Gets the target reference white. When not set, <see cref="HunterLab.DefaultWhitePoint"/> is used.
/// </summary>
public CieXyz HunterLabWhitePoint { get; }
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="HunterLab"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public HunterLab Convert(in CieXyz input)
{
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab

20
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs

@ -2,13 +2,14 @@
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between CieXyz and LinearRgb
/// Color converter between <see cref="CieXyz"/> and <see cref="LinearRgb"/>
/// </summary>
internal sealed class CieXyzToLinearRgbConverter : LinearRgbAndCieXyzConverterBase, IColorConversion<CieXyz, LinearRgb>
internal sealed class CieXyzToLinearRgbConverter : LinearRgbAndCieXyzConverterBase
{
private readonly Matrix4x4 conversionMatrix;
@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// Initializes a new instance of the <see cref="CieXyzToLinearRgbConverter"/> class.
/// </summary>
/// <param name="workingSpace">The target working space.</param>
public CieXyzToLinearRgbConverter(RgbWorkingSpace workingSpace)
public CieXyzToLinearRgbConverter(RgbWorkingSpaceBase workingSpace)
{
this.TargetWorkingSpace = workingSpace;
this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace);
@ -33,13 +34,18 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <summary>
/// Gets the target working space
/// </summary>
public RgbWorkingSpace TargetWorkingSpace { get; }
public RgbWorkingSpaceBase TargetWorkingSpace { get; }
/// <inheritdoc/>
/// <summary>
/// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="LinearRgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb Convert(in CieXyz input)
{
Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted);
Vector3 vector = Vector3.Transform(input.Vector, inverted);
var vector = Vector3.Transform(input.ToVector3(), inverted);
return new LinearRgb(vector, this.TargetWorkingSpace);
}
}

51
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs

@ -0,0 +1,51 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between <see cref="Cmyk"/> and <see cref="Rgb"/>
/// </summary>
internal sealed class CmykAndRgbConverter
{
/// <summary>
/// Performs the conversion from the <see cref="Cmyk"/> input to an instance of <see cref="Rgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in Cmyk input)
{
Vector3 rgb = (Vector3.One - new Vector3(input.C, input.M, input.Y)) * (Vector3.One - new Vector3(input.K));
return new Rgb(rgb);
}
/// <summary>
/// Performs the conversion from the <see cref="Rgb"/> input to an instance of <see cref="Cmyk"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Cmyk Convert(in Rgb input)
{
// To CMY
Vector3 cmy = Vector3.One - input.ToVector3();
// To CMYK
var k = new Vector3(MathF.Min(cmy.X, MathF.Min(cmy.Y, cmy.Z)));
if (MathF.Abs(k.X - 1F) < Constants.Epsilon)
{
return new Cmyk(0, 0, 0, 1F);
}
cmy = (cmy - k) / (Vector3.One - k);
return new Cmyk(cmy.X, cmy.Y, cmy.Z, k.X);
}
}
}

28
src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs

@ -4,16 +4,20 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between HSL and Rgb
/// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas.
/// </summary>
internal class HslAndRgbConverter : IColorConversion<Hsl, Rgb>, IColorConversion<Rgb, Hsl>
internal sealed class HslAndRgbConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="Hsl"/> input to an instance of <see cref="Rgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in Hsl input)
{
float rangedH = input.H / 360F;
@ -43,8 +47,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap
return new Rgb(r, g, b);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="Rgb"/> input to an instance of <see cref="Hsl"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Hsl Convert(in Rgb input)
{
float r = input.R;
@ -103,7 +111,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static float GetColorComponent(float first, float second, float third)
{
third = MoveIntoRange(third);
@ -134,16 +142,16 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static float MoveIntoRange(float value)
{
if (value < 0F)
{
value += 1F;
value++;
}
else if (value > 1F)
{
value -= 1F;
value--;
}
return value;

20
src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs

@ -4,16 +4,20 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between HSV and Rgb
/// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas.
/// </summary>
internal class HsvAndRgbConverter : IColorConversion<Hsv, Rgb>, IColorConversion<Rgb, Hsv>
internal sealed class HsvAndRgbConverter
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="Hsv"/> input to an instance of <see cref="Rgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in Hsv input)
{
float s = input.S;
@ -75,8 +79,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSap
return new Rgb(r, g, b);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="Rgb"/> input to an instance of <see cref="Hsv"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Hsv Convert(in Rgb input)
{
float r = input.R;

16
src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs

@ -4,15 +4,19 @@
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between HunterLab and CieXyz
/// Color converter between <see cref="HunterLab"/> and <see cref="CieXyz"/>
/// </summary>
internal class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase, IColorConversion<HunterLab, CieXyz>
internal sealed class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="HunterLab"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in HunterLab input)
{
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
@ -22,7 +26,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo
float ka = ComputeKa(input.WhitePoint);
float kb = ComputeKb(input.WhitePoint);
float y = MathF.Pow(l / 100F, 2) * yn;
float y = ImageMaths.Pow2(l / 100F) * yn;
float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn;
float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn);

33
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs

@ -3,10 +3,10 @@
using System.Numerics;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Provides base methods for converting between Rgb and CieXyz color spaces.
/// Provides base methods for converting between <see cref="LinearRgb"/> and <see cref="CieXyz"/> color spaces.
/// </summary>
internal abstract class LinearRgbAndCieXyzConverterBase
{
@ -15,10 +15,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// </summary>
/// <param name="workingSpace">The Rgb working space.</param>
/// <returns>The <see cref="Matrix4x4"/> based on the chromaticity and working space.</returns>
public static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpace workingSpace)
public static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpaceBase workingSpace)
{
DebugGuard.NotNull(workingSpace, nameof(workingSpace));
RgbPrimariesChromaticityCoordinates chromaticity = workingSpace.ChromaticityCoordinates;
float xr = chromaticity.R.X;
@ -42,23 +41,35 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
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);
Vector3 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
};
}

20
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs

@ -2,13 +2,14 @@
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between LinearRgb and CieXyz
/// Color converter between <see cref="LinearRgb"/> and <see cref="CieXyz"/>
/// </summary>
internal sealed class LinearRgbToCieXyzConverter : LinearRgbAndCieXyzConverterBase, IColorConversion<LinearRgb, CieXyz>
internal sealed class LinearRgbToCieXyzConverter : LinearRgbAndCieXyzConverterBase
{
private readonly Matrix4x4 conversionMatrix;
@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// Initializes a new instance of the <see cref="LinearRgbToCieXyzConverter"/> class.
/// </summary>
/// <param name="workingSpace">The target working space.</param>
public LinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace)
public LinearRgbToCieXyzConverter(RgbWorkingSpaceBase workingSpace)
{
this.SourceWorkingSpace = workingSpace;
this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace);
@ -33,14 +34,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <summary>
/// Gets the source working space
/// </summary>
public RgbWorkingSpace SourceWorkingSpace { get; }
public RgbWorkingSpaceBase SourceWorkingSpace { get; }
/// <inheritdoc/>
/// <summary>
/// Performs the conversion from the <see cref="LinearRgb"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in LinearRgb input)
{
DebugGuard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal.");
Vector3 vector = Vector3.Transform(input.Vector, this.conversionMatrix);
var vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix);
return new CieXyz(vector);
}
}

29
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between <see cref="LinearRgb"/> and <see cref="Rgb"/>
/// </summary>
internal sealed class LinearRgbToRgbConverter
{
/// <summary>
/// Performs the conversion from the <see cref="LinearRgb"/> input to an instance of <see cref="Rgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in LinearRgb input)
{
var vector = input.ToVector3();
vector.X = input.WorkingSpace.Compress(vector.X);
vector.Y = input.WorkingSpace.Compress(vector.Y);
vector.Z = input.WorkingSpace.Compress(vector.Z);
return new Rgb(vector, input.WorkingSpace);
}
}
}

29
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between Rgb and LinearRgb
/// </summary>
internal class RgbToLinearRgbConverter
{
/// <summary>
/// Performs the conversion from the <see cref="Rgb"/> input to an instance of <see cref="LinearRgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb Convert(in Rgb input)
{
var vector = input.ToVector3();
vector.X = input.WorkingSpace.Expand(vector.X);
vector.Y = input.WorkingSpace.Expand(vector.Y);
vector.Z = input.WorkingSpace.Expand(vector.Z);
return new LinearRgb(vector, input.WorkingSpace);
}
}
}

24
src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs

@ -5,18 +5,22 @@ using System;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Color converter between YCbCr and Rgb
/// Color converter between <see cref="YCbCr"/> and <see cref="Rgb"/>
/// See <see href="https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion"/> for formulas.
/// </summary>
internal class YCbCrAndRgbConverter : IColorConversion<YCbCr, Rgb>, IColorConversion<Rgb, YCbCr>
internal sealed class YCbCrAndRgbConverter
{
private static readonly Vector3 MaxBytes = new Vector3(255F);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="YCbCr"/> input to an instance of <see cref="Rgb"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in YCbCr input)
{
float y = input.Y;
@ -30,11 +34,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorS
return new Rgb(new Vector3(r, g, b) / MaxBytes);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Performs the conversion from the <see cref="Rgb"/> input to an instance of <see cref="YCbCr"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
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;

97
src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs

@ -3,11 +3,10 @@
using System.Numerics;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// AdaptionMatrix3X3 used for transformation from XYZ to LMS, defining the cone response domain.
/// Matrices used for transformation from <see cref="CieXyz"/> to <see cref="Lms"/>, defining the cone response domain.
/// Used in <see cref="IChromaticAdaptation"/>
/// </summary>
/// <remarks>
@ -17,7 +16,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
/// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy
/// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf
/// </remarks>
internal static class LmsAdaptationMatrix
public static class LmsAdaptationMatrix
{
/// <summary>
/// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65)
@ -25,9 +24,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
public static readonly Matrix4x4 VonKriesHPEAdjusted
= Matrix4x4.Transpose(new Matrix4x4
{
M11 = 0.40024F, M12 = 0.7076F, M13 = -0.08081F,
M21 = -0.2263F, M22 = 1.16532F, M23 = 0.0457F,
M31 = 0, M32 = 0, M33 = 0.91822F,
M11 = 0.40024F,
M12 = 0.7076F,
M13 = -0.08081F,
M21 = -0.2263F,
M22 = 1.16532F,
M23 = 0.0457F,
M31 = 0,
M32 = 0,
M33 = 0.91822F,
M44 = 1F // Important for inverse transforms.
});
@ -37,9 +42,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
public static readonly Matrix4x4 VonKriesHPE
= Matrix4x4.Transpose(new Matrix4x4
{
M11 = 0.3897F, M12 = 0.6890F, M13 = -0.0787F,
M21 = -0.2298F, M22 = 1.1834F, M23 = 0.0464F,
M31 = 0, M32 = 0, M33 = 1F,
M11 = 0.3897F,
M12 = 0.6890F,
M13 = -0.0787F,
M21 = -0.2298F,
M22 = 1.1834F,
M23 = 0.0464F,
M31 = 0,
M32 = 0,
M33 = 1F,
M44 = 1F
});
@ -54,9 +65,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
public static readonly Matrix4x4 Bradford
= Matrix4x4.Transpose(new Matrix4x4
{
M11 = 0.8951F, M12 = 0.2664F, M13 = -0.1614F,
M21 = -0.7502F, M22 = 1.7135F, M23 = 0.0367F,
M31 = 0.0389F, M32 = -0.0685F, M33 = 1.0296F,
M11 = 0.8951F,
M12 = 0.2664F,
M13 = -0.1614F,
M21 = -0.7502F,
M22 = 1.7135F,
M23 = 0.0367F,
M31 = 0.0389F,
M32 = -0.0685F,
M33 = 1.0296F,
M44 = 1F
});
@ -65,35 +82,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
/// </summary>
public static readonly Matrix4x4 BradfordSharp
= Matrix4x4.Transpose(new Matrix4x4
{
M11 = 1.2694F, M12 = -0.0988F, M13 = -0.1706F,
M21 = -0.8364F, M22 = 1.8006F, M23 = 0.0357F,
M31 = 0.0297F, M32 = -0.0315F, M33 = 1.0018F,
M44 = 1F
});
{
M11 = 1.2694F,
M12 = -0.0988F,
M13 = -0.1706F,
M21 = -0.8364F,
M22 = 1.8006F,
M23 = 0.0357F,
M31 = 0.0297F,
M32 = -0.0315F,
M33 = 1.0018F,
M44 = 1F
});
/// <summary>
/// CMCCAT2000 (fitted from all available color data sets)
/// </summary>
public static readonly Matrix4x4 CMCCAT2000
= Matrix4x4.Transpose(new Matrix4x4
{
M11 = 0.7982F, M12 = 0.3389F, M13 = -0.1371F,
M21 = -0.5918F, M22 = 1.5512F, M23 = 0.0406F,
M31 = 0.0008F, M32 = 0.239F, M33 = 0.9753F,
M44 = 1F
});
{
M11 = 0.7982F,
M12 = 0.3389F,
M13 = -0.1371F,
M21 = -0.5918F,
M22 = 1.5512F,
M23 = 0.0406F,
M31 = 0.0008F,
M32 = 0.239F,
M33 = 0.9753F,
M44 = 1F
});
/// <summary>
/// CAT02 (optimized for minimizing CIELAB differences)
/// </summary>
public static readonly Matrix4x4 CAT02
= Matrix4x4.Transpose(new Matrix4x4
{
M11 = 0.7328F, M12 = 0.4296F, M13 = -0.1624F,
M21 = -0.7036F, M22 = 1.6975F, M23 = 0.0061F,
M31 = 0.0030F, M32 = 0.0136F, M33 = 0.9834F,
M44 = 1F
});
{
M11 = 0.7328F,
M12 = 0.4296F,
M13 = -0.1624F,
M21 = -0.7036F,
M22 = 1.6975F,
M23 = 0.0061F,
M31 = 0.0030F,
M32 = 0.0136F,
M33 = 0.9834F,
M44 = 1F
});
}
}

21
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs → src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs

@ -3,13 +3,13 @@
using System;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Represents the chromaticity coordinates of RGB primaries.
/// One of the specifiers of <see cref="RgbWorkingSpace"/>.
/// One of the specifiers of <see cref="RgbWorkingSpaceBase"/>.
/// </summary>
internal readonly struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates>
public readonly struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates>
{
/// <summary>
/// Initializes a new instance of the <see cref="RgbPrimariesChromaticityCoordinates"/> struct.
@ -40,13 +40,13 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
public CieXyChromaticityCoordinates B { get; }
/// <summary>
/// Compares two <see cref="CieLab"/> objects for equality.
/// Compares two <see cref="RgbPrimariesChromaticityCoordinates"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieLab"/> on the left side of the operand.
/// The <see cref="RgbPrimariesChromaticityCoordinates"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLab"/> on the right side of the operand.
/// The <see cref="RgbPrimariesChromaticityCoordinates"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
@ -57,13 +57,13 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
}
/// <summary>
/// Compares two <see cref="CieLab"/> objects for inequality
/// Compares two <see cref="RgbPrimariesChromaticityCoordinates"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="CieLab"/> on the left side of the operand.
/// The <see cref="RgbPrimariesChromaticityCoordinates"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLab"/> on the right side of the operand.
/// The <see cref="RgbPrimariesChromaticityCoordinates"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
@ -92,8 +92,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
{
int hashCode = this.R.GetHashCode();
hashCode = (hashCode * 397) ^ this.G.GetHashCode();
hashCode = (hashCode * 397) ^ this.B.GetHashCode();
return hashCode;
return (hashCode * 397) ^ this.B.GetHashCode();
}
}
}

46
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs

@ -1,46 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Implements gamma companding
/// </summary>
/// <remarks>
/// <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"/>
/// </remarks>
internal class GammaCompanding : ICompanding
{
/// <summary>
/// Initializes a new instance of the <see cref="GammaCompanding"/> class.
/// </summary>
/// <param name="gamma">The gamma value.</param>
public GammaCompanding(float gamma)
{
this.Gamma = gamma;
}
/// <summary>
/// Gets the gamma value
/// </summary>
public float Gamma { get; }
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Expand(float channel)
{
return MathF.Pow(channel, this.Gamma);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Compress(float channel)
{
return MathF.Pow(channel, 1 / this.Gamma);
}
}
}

35
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs

@ -1,35 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Implements L* companding
/// </summary>
/// <remarks>
/// For more info see:
/// <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"/>
/// </remarks>
internal class LCompanding : ICompanding
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Expand(float channel)
{
return channel <= 0.08 ? 100 * channel / CieConstants.Kappa : MathF.Pow((channel + 0.16F) / 1.16F, 3);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Compress(float channel)
{
return channel <= CieConstants.Epsilon
? channel * CieConstants.Kappa / 100F
: MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F;
}
}
}

24
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs

@ -1,24 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Color converter between LinearRgb and Rgb
/// </summary>
internal class LinearRgbToRgbConverter : IColorConversion<LinearRgb, Rgb>
{
/// <inheritdoc/>
public Rgb Convert(in LinearRgb input)
{
Vector3 vector = input.Vector;
vector.X = input.WorkingSpace.Companding.Compress(vector.X);
vector.Y = input.WorkingSpace.Companding.Compress(vector.Y);
vector.Z = input.WorkingSpace.Companding.Compress(vector.Z);
return new Rgb(vector, input.WorkingSpace);
}
}
}

32
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs

@ -1,32 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Implements Rec. 2020 companding function (for 12-bits).
/// </summary>
/// <remarks>
/// <see href="http://en.wikipedia.org/wiki/Rec._2020"/>
/// For 10-bits, companding is identical to <see cref="Rec709Companding"/>
/// </remarks>
internal class Rec2020Companding : ICompanding
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Expand(float channel)
{
return channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Compress(float channel)
{
return channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F;
}
}
}

31
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs

@ -1,31 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Implements the Rec. 709 companding function
/// </summary>
/// <remarks>
/// http://en.wikipedia.org/wiki/Rec._709
/// </remarks>
internal class Rec709Companding : ICompanding
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Expand(float channel)
{
return channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Compress(float channel)
{
return channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F;
}
}
}

24
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs

@ -1,24 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Color converter between Rgb and LinearRgb
/// </summary>
internal class RgbToLinearRgbConverter : IColorConversion<Rgb, LinearRgb>
{
/// <inheritdoc/>
public LinearRgb Convert(in Rgb input)
{
Vector3 vector = input.Vector;
vector.X = input.WorkingSpace.Companding.Expand(vector.X);
vector.Y = input.WorkingSpace.Companding.Expand(vector.Y);
vector.Z = input.WorkingSpace.Companding.Expand(vector.Z);
return new LinearRgb(vector, input.WorkingSpace);
}
}
}

98
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs

@ -1,98 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Trivial implementation of <see cref="RgbWorkingSpace"/>
/// </summary>
internal class RgbWorkingSpace
{
/// <summary>
/// Initializes a new instance of the <see cref="RgbWorkingSpace"/> class.
/// </summary>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="companding">The function pair for converting to <see cref="CieXyz"/> and back.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
public RgbWorkingSpace(CieXyz referenceWhite, ICompanding companding, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
{
this.WhitePoint = referenceWhite;
this.Companding = companding;
this.ChromaticityCoordinates = chromaticityCoordinates;
}
/// <summary>
/// Gets the reference white point
/// </summary>
public CieXyz WhitePoint { get; }
/// <summary>
/// Gets the function pair for converting to <see cref="CieXyz"/> and back.
/// </summary>
public ICompanding Companding { get; }
/// <summary>
/// Gets the chromaticity of the rgb primaries.
/// </summary>
public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; }
/// <summary>
/// Compares two <see cref="RgbWorkingSpace"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="RgbWorkingSpace"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="RgbWorkingSpace"/> 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 ==(RgbWorkingSpace left, RgbWorkingSpace right)
{
return Equals(left, right);
}
/// <summary>
/// Compares two <see cref="RgbWorkingSpace"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="RgbWorkingSpace"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="RgbWorkingSpace"/> 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 !=(RgbWorkingSpace left, RgbWorkingSpace right)
{
return !Equals(left, right);
}
public override bool Equals(object obj)
{
return obj is RgbWorkingSpace other && this.Equals(other);
}
public bool Equals(RgbWorkingSpace other)
{
// TODO: Object.Equals for ICompanding will be slow.
return this.WhitePoint.Equals(other.WhitePoint)
&& this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates)
&& Equals(this.Companding, other.Companding);
}
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = this.WhitePoint.GetHashCode();
hashCode = (hashCode * 397) ^ this.ChromaticityCoordinates.GetHashCode();
hashCode = (hashCode * 397) ^ (this.Companding?.GetHashCode() ?? 0);
return hashCode;
}
}
}
}

33
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs

@ -1,33 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce
{
/// <summary>
/// Implements sRGB companding
/// </summary>
/// <remarks>
/// For more info see:
/// <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"/>
/// </remarks>
internal class SRgbCompanding : ICompanding
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Expand(float channel)
{
return channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Compress(float channel)
{
return channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F;
}
}
}

36
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaCompanding.cs

@ -0,0 +1,36 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Implements gamma companding
/// </summary>
/// <remarks>
/// <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"/>
/// </remarks>
public static class GammaCompanding
{
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <param name="gamma">The gamma value.</param>
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel, float gamma) => MathF.Pow(channel, gamma);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <param name="gamma">The gamma value.</param>
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel, float gamma) => MathF.Pow(channel, 1 / gamma);
}
}

65
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs

@ -0,0 +1,65 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// The gamma working space.
/// </summary>
public class GammaWorkingSpace : RgbWorkingSpaceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="GammaWorkingSpace" /> class.
/// </summary>
/// <param name="gamma">The gamma value.</param>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
public GammaWorkingSpace(float gamma, CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
: base(referenceWhite, chromaticityCoordinates) => this.Gamma = gamma;
/// <summary>
/// Gets the gamma value.
/// </summary>
public float Gamma { get; }
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Compress(float channel) => GammaCompanding.Compress(channel, this.Gamma);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Expand(float channel) => GammaCompanding.Expand(channel, this.Gamma);
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj is GammaWorkingSpace other)
{
return this.Gamma.Equals(other.Gamma)
&& this.WhitePoint.Equals(other.WhitePoint)
&& this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates);
}
return false;
}
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = base.GetHashCode();
return HashHelpers.Combine(hash, this.Gamma.GetHashCode());
}
}
}

37
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LCompanding.cs

@ -0,0 +1,37 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Implements L* companding
/// </summary>
/// <remarks>
/// For more info see:
/// <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"/>
/// </remarks>
public static class LCompanding
{
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel)
=> channel <= 0.08 ? 100 * channel / CieConstants.Kappa : ImageMaths.Pow3((channel + 0.16F) / 1.16F);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
/// </summary>
/// <param name="channel">The channel value</param>
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel)
=> channel <= CieConstants.Epsilon ? channel * CieConstants.Kappa / 100F : MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F;
}
}

31
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// L* working space.
/// </summary>
public sealed class LWorkingSpace : RgbWorkingSpaceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="LWorkingSpace" /> class.
/// </summary>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
public LWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
: base(referenceWhite, chromaticityCoordinates)
{
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Compress(float channel) => LCompanding.Compress(channel);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Expand(float channel) => LCompanding.Expand(channel);
}
}

36
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020Companding.cs

@ -0,0 +1,36 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Implements Rec. 2020 companding function (for 12-bits).
/// </summary>
/// <remarks>
/// <see href="http://en.wikipedia.org/wiki/Rec._2020"/>
/// For 10-bits, companding is identical to <see cref="Rec709Companding"/>
/// </remarks>
public static class Rec2020Companding
{
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel)
=> channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel)
=> channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F;
}
}

31
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space.
/// </summary>
public sealed class Rec2020WorkingSpace : RgbWorkingSpaceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="Rec2020WorkingSpace" /> class.
/// </summary>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
public Rec2020WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
: base(referenceWhite, chromaticityCoordinates)
{
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Compress(float channel) => Rec2020Companding.Compress(channel);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Expand(float channel) => Rec2020Companding.Expand(channel);
}
}

35
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709Companding.cs

@ -0,0 +1,35 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Implements the Rec. 709 companding function.
/// </summary>
/// <remarks>
/// http://en.wikipedia.org/wiki/Rec._709
/// </remarks>
public static class Rec709Companding
{
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel)
=> channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
/// </summary>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel)
=> channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F;
}
}

31
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Rec. 709 (ITU-R Recommendation BT.709) working space.
/// </summary>
public sealed class Rec709WorkingSpace : RgbWorkingSpaceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="Rec709WorkingSpace" /> class.
/// </summary>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
public Rec709WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
: base(referenceWhite, chromaticityCoordinates)
{
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Compress(float channel) => Rec709Companding.Compress(channel);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Expand(float channel) => Rec709Companding.Expand(channel);
}
}

83
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs

@ -0,0 +1,83 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Base class for all implementations of <see cref="RgbWorkingSpaceBase"/>.
/// </summary>
public abstract class RgbWorkingSpaceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="RgbWorkingSpaceBase"/> class.
/// </summary>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
protected RgbWorkingSpaceBase(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
{
this.WhitePoint = referenceWhite;
this.ChromaticityCoordinates = chromaticityCoordinates;
}
/// <summary>
/// Gets the reference white point
/// </summary>
public CieXyz WhitePoint { get; }
/// <summary>
/// Gets the chromaticity of the rgb primaries.
/// </summary>
public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; }
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <remarks>
/// For more info see:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// </remarks>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
public abstract float Expand(float channel);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent (depends on the RGB color system).
/// </summary>
/// <remarks>
/// For more info see:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
/// <param name="channel">The channel value.</param>
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
public abstract float Compress(float channel);
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj is RgbWorkingSpaceBase other)
{
return this.WhitePoint.Equals(other.WhitePoint)
&& this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates);
}
return false;
}
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = this.WhitePoint.GetHashCode();
return HashHelpers.Combine(hash, this.ChromaticityCoordinates.GetHashCode());
}
}
}

35
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbCompanding.cs

@ -0,0 +1,35 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// Implements sRGB companding
/// </summary>
/// <remarks>
/// For more info see:
/// <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"/>
/// </remarks>
public static class SRgbCompanding
{
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <param name="channel">The channel value</param>
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel) => channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
/// </summary>
/// <param name="channel">The channel value</param>
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static float Compress(float channel) => channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F;
}
}

31
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
/// The sRgb working space.
/// </summary>
public sealed class SRgbWorkingSpace : RgbWorkingSpaceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="SRgbWorkingSpace" /> class.
/// </summary>
/// <param name="referenceWhite">The reference white point.</param>
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
public SRgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
: base(referenceWhite, chromaticityCoordinates)
{
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Compress(float channel) => SRgbCompanding.Compress(channel);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override float Expand(float channel) => SRgbCompanding.Expand(channel);
}
}

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

@ -1,8 +1,11 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
@ -13,7 +16,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// Transformation described here:
/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
/// </remarks>
internal class VonKriesChromaticAdaptation : IChromaticAdaptation
public class VonKriesChromaticAdaptation : IChromaticAdaptation
{
private readonly CieXyzAndLmsConverter converter;
@ -41,27 +44,54 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
/// </summary>
/// <param name="converter">The color converter</param>
public VonKriesChromaticAdaptation(CieXyzAndLmsConverter converter)
{
this.converter = converter;
}
internal VonKriesChromaticAdaptation(CieXyzAndLmsConverter converter) => this.converter = converter;
/// <inheritdoc/>
public CieXyz Transform(in CieXyz sourceColor, in CieXyz sourceWhitePoint, in CieXyz targetWhitePoint)
public CieXyz Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint)
{
if (sourceWhitePoint.Equals(targetWhitePoint))
if (sourceWhitePoint.Equals(destinationWhitePoint))
{
return sourceColor;
return source;
}
Lms sourceColorLms = this.converter.Convert(sourceColor);
Lms sourceColorLms = this.converter.Convert(source);
Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint);
Lms targetWhitePointLms = this.converter.Convert(targetWhitePoint);
Lms targetWhitePointLms = this.converter.Convert(destinationWhitePoint);
var vector = new Vector3(targetWhitePointLms.L / sourceWhitePointLms.L, targetWhitePointLms.M / sourceWhitePointLms.M, targetWhitePointLms.S / sourceWhitePointLms.S);
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);
}
/// <inheritdoc/>
public void Transform(Span<CieXyz> source, Span<CieXyz> destination, CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint, int count)
{
Guard.SpansMustBeSizedAtLeast(source, nameof(source), destination, nameof(destination), count);
if (sourceWhitePoint.Equals(destinationWhitePoint))
{
source.CopyTo(destination.Slice(0, count));
return;
}
ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source);
ref CieXyz destRef = ref MemoryMarshal.GetReference(destination);
for (int i = 0; i < count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
Lms sourceColorLms = this.converter.Convert(sp);
Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint);
Lms targetWhitePointLms = this.converter.Convert(destinationWhitePoint);
Vector3 vector = targetWhitePointLms.ToVector3() / sourceWhitePointLms.ToVector3();
var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.ToVector3()));
dp = this.converter.Convert(targetColorLms);
}
}
}
}

125
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,17 +10,28 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a Hsl (hue, saturation, lightness) color.
/// </summary>
internal readonly struct Hsl : IColorVector, IEquatable<Hsl>, IAlmostEquatable<Hsl, float>
public readonly struct Hsl : IEquatable<Hsl>
{
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = new Vector3(360, 1, 1);
/// <summary>
/// Max range used for clamping.
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
private static readonly Vector3 VectorMax = new Vector3(360, 1, 1);
public readonly float H;
/// <summary>
/// The backing vector for SIMD support.
/// 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.
@ -29,7 +39,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="h">The h hue component.</param>
/// <param name="s">The s saturation component.</param>
/// <param name="l">The l value (lightness) component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public Hsl(float h, float s, float l)
: this(new Vector3(h, s, l))
{
@ -39,118 +49,61 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="Hsl"/> struct.
/// </summary>
/// <param name="vector">The vector representing the h, s, l components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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;
vector = Vector3.Clamp(vector, Min, Max);
this.H = vector.X;
this.S = vector.Y;
this.L = vector.Z;
}
/// <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 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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(Hsl left, Hsl right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "Hsl [ Empty ]"
: $"Hsl [ H={this.H:#0.##}, S={this.S:#0.##}, L={this.L:#0.##} ]";
}
public override string ToString() => $"Hsl({this.H:#0.##}, {this.S:#0.##}, {this.L:#0.##})";
/// <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)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
}
}

181
src/ImageSharp/ColorSpaces/Hsv.cs

@ -2,28 +2,36 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
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>
public readonly struct Hsv : IEquatable<Hsv>
{
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = new Vector3(360, 1, 1);
/// <summary>
/// Max range used for clamping.
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
private static readonly Vector3 VectorMax = new Vector3(360, 1, 1);
public readonly float H;
/// <summary>
/// The backing vector for SIMD support.
/// 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 value (brightness) component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float V;
/// <summary>
/// Initializes a new instance of the <see cref="Hsv"/> struct.
@ -31,7 +39,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="h">The h hue component.</param>
/// <param name="s">The s saturation component.</param>
/// <param name="v">The v value (brightness) component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public Hsv(float h, float s, float v)
: this(new Vector3(h, s, v))
{
@ -41,168 +49,59 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="Hsv"/> struct.
/// </summary>
/// <param name="vector">The vector representing the h, s, v components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public Hsv(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;
}
/// <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"/>.
/// </summary>
/// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Hsv"/>.
/// </returns>
public static implicit operator Hsv(Rgba32 color)
{
float r = color.R / 255F;
float g = color.G / 255F;
float b = color.B / 255F;
float max = MathF.Max(r, MathF.Max(g, b));
float min = MathF.Min(r, MathF.Min(g, b));
float chroma = max - min;
float h = 0;
float s = 0;
float v = max;
if (MathF.Abs(chroma) < Constants.Epsilon)
{
return new Hsv(0, s, v);
}
if (MathF.Abs(r - max) < Constants.Epsilon)
{
h = (g - b) / chroma;
}
else if (MathF.Abs(g - max) < Constants.Epsilon)
{
h = 2 + ((b - r) / chroma);
}
else if (MathF.Abs(b - max) < Constants.Epsilon)
{
h = 4 + ((r - g) / chroma);
}
h *= 60;
if (h < 0.0)
{
h += 360;
}
s = chroma / v;
return new Hsv(h, s, v);
vector = Vector3.Clamp(vector, Min, Max);
this.H = vector.X;
this.S = vector.Y;
this.V = vector.Z;
}
/// <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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(Hsv left, Hsv right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "Hsv [ Empty ]"
: $"Hsv [ H={this.H:#0.##}, S={this.S:#0.##}, V={this.V:#0.##} ]";
}
public override string ToString() => $"Hsv({this.H:#0.##}, {this.S:#0.##}, {this.V:#0.##})";
/// <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)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
}
}

142
src/ImageSharp/ColorSpaces/HunterLab.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 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>
public readonly struct HunterLab : IEquatable<HunterLab>
{
/// <summary>
/// D50 standard illuminant.
@ -21,9 +20,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 usually 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 usually 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 usually 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="HunterLab"/> struct.
@ -32,7 +49,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="a">The a (green - magenta) component.</param>
/// <param name="b">The b (blue - yellow) component.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public HunterLab(float l, float a, float b)
: this(new Vector3(l, a, b), DefaultWhitePoint)
{
@ -45,7 +62,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="a">The a (green - magenta) component.</param>
/// <param name="b">The b (blue - yellow) component.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public HunterLab(float l, float a, float b, CieXyz whitePoint)
: this(new Vector3(l, a, b), whitePoint)
{
@ -56,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l, a, b components.</param>
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public HunterLab(Vector3 vector)
: this(vector, DefaultWhitePoint)
{
@ -67,126 +84,61 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the l a b components.</param>
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public HunterLab(Vector3 vector, CieXyz whitePoint)
: this()
{
this.backingVector = vector;
// Not clamping as documentation about this space only indicates "usual" ranges
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(HunterLab left, HunterLab right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "HunterLab [Empty]"
: $"HunterLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]";
}
public override string ToString() => $"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})";
/// <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)]
[MethodImpl(InliningOptions.ShortMethod)]
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;
}
}
}

28
src/ImageSharp/ColorSpaces/IAlmostEquatable.cs

@ -1,28 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Defines a generalized method that a value type or class implements to create
/// a type-specific method for determining approximate equality of instances.
/// </summary>
/// <typeparam name="TPixel">The type of objects to compare.</typeparam>
/// <typeparam name="TPrecision">The object specifying the type to specify precision with.</typeparam>
internal interface IAlmostEquatable<in TPixel, in TPrecision>
where TPrecision : struct, IComparable<TPrecision>
{
/// <summary>
/// Indicates whether the current object is equal to another object of the same type
/// when compared to the specified precision level.
/// </summary>
/// <param name="other">An object to compare with this object.</param>
/// <param name="precision">The object specifying the level of precision.</param>
/// <returns>
/// true if the current object is equal to the other parameter; otherwise, false.
/// </returns>
bool AlmostEquals(TPixel other, TPrecision 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; }
}
}

37
src/ImageSharp/ColorSpaces/ICompanding.cs

@ -1,37 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Pair of companding functions for <see cref="RgbWorkingSpace"/>.
/// Used for conversion to <see cref="CieXyz"/> and backwards.
/// See also: <seealso cref="RgbWorkingSpace.Companding"/>
/// </summary>
internal interface ICompanding
{
/// <summary>
/// Expands a companded channel to its linear equivalent with respect to the energy.
/// </summary>
/// <remarks>
/// For more info see:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// </remarks>
/// <param name="channel">The channel value</param>
/// <returns>The linear channel value</returns>
float Expand(float channel);
/// <summary>
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent (depends on the RGB color system).
/// </summary>
/// <remarks>
/// For more info see:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
/// <param name="channel">The channel value</param>
/// <returns>The nonlinear channel value</returns>
float Compress(float channel);
}
}

2
src/ImageSharp/ColorSpaces/Illuminants.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Descriptions taken from:
/// http://en.wikipedia.org/wiki/Standard_illuminant
/// </remarks>
internal static class Illuminants
public static class Illuminants
{
/// <summary>
/// Incandescent / Tungsten

163
src/ImageSharp/ColorSpaces/LinearRgb.cs

@ -4,24 +4,45 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Represents an linear Rgb color with specified <see cref="RgbWorkingSpace"/> working space
/// Represents an linear Rgb color with specified <see cref="RgbWorkingSpaceBase"/> working space
/// </summary>
internal readonly struct LinearRgb : IColorVector, IEquatable<LinearRgb>, IAlmostEquatable<LinearRgb, float>
public readonly struct LinearRgb : IEquatable<LinearRgb>
{
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = Vector3.One;
/// <summary>
/// The default LinearRgb working space.
/// </summary>
public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
public static readonly RgbWorkingSpaceBase DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
/// <summary>
/// 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>
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>
/// The backing vector for SIMD support.
/// Gets the LinearRgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary>
private readonly Vector3 backingVector;
public readonly RgbWorkingSpaceBase WorkingSpace;
/// <summary>
/// Initializes a new instance of the <see cref="LinearRgb"/> struct.
@ -29,9 +50,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="r">The red component ranging between 0 and 1.</param>
/// <param name="g">The green component ranging between 0 and 1.</param>
/// <param name="b">The blue component ranging between 0 and 1.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb(float r, float g, float b)
: this(new Vector3(r, g, b))
: this(r, g, b, DefaultWorkingSpace)
{
}
@ -42,8 +63,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="g">The green component ranging between 0 and 1.</param>
/// <param name="b">The blue component ranging between 0 and 1.</param>
/// <param name="workingSpace">The rgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LinearRgb(float r, float g, float b, RgbWorkingSpace workingSpace)
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb(float r, float g, float b, RgbWorkingSpaceBase workingSpace)
: this(new Vector3(r, g, b), workingSpace)
{
}
@ -52,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="LinearRgb"/> struct.
/// </summary>
/// <param name="vector">The vector representing the r, g, b components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb(Vector3 vector)
: this(vector, DefaultWorkingSpace)
{
@ -63,126 +84,68 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the r, g, b components.</param>
/// <param name="workingSpace">The LinearRgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LinearRgb(Vector3 vector, RgbWorkingSpace workingSpace)
: this()
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb(Vector3 vector, RgbWorkingSpaceBase workingSpace)
{
// Clamp to 0-1 range.
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One);
vector = Vector3.Clamp(vector, Min, Max);
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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(InliningOptions.ShortMethod)]
public Vector3 ToVector3() => new Vector3(this.R, this.G, this.B);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "LinearRgb [ Empty ]"
: $"LinearRgb [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##} ]";
}
public override string ToString() => $"LinearRgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})";
/// <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)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
}
}

134
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>
public 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.
@ -26,7 +38,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="l">L represents the responsivity at long wavelengths.</param>
/// <param name="m">M represents the responsivity at medium wavelengths.</param>
/// <param name="s">S represents the responsivity at short wavelengths.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public Lms(float l, float m, float s)
: this(new Vector3(l, m, s))
{
@ -36,119 +48,65 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="Lms"/> struct.
/// </summary>
/// <param name="vector">The vector representing the l, m, s components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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;
// Not clamping as documentation about this space only indicates "usual" ranges
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "Lms [ Empty ]"
: $"Lms [ L={this.L:#0.##}, M={this.M:#0.##}, S={this.S:#0.##} ]";
}
public override string ToString() => $"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})";
/// <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)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
}
}

172
src/ImageSharp/ColorSpaces/Rgb.cs

@ -4,25 +4,46 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
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="RgbWorkingSpaceBase"/> working space.
/// </summary>
internal readonly struct Rgb : IColorVector, IEquatable<Rgb>, IAlmostEquatable<Rgb, float>
public readonly struct Rgb : IEquatable<Rgb>
{
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = Vector3.One;
/// <summary>
/// The default rgb working space
/// </summary>
public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
public static readonly RgbWorkingSpaceBase 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>
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>
private readonly Vector3 backingVector;
public readonly RgbWorkingSpaceBase WorkingSpace;
/// <summary>
/// Initializes a new instance of the <see cref="Rgb"/> struct.
@ -30,9 +51,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="r">The red component ranging between 0 and 1.</param>
/// <param name="g">The green component ranging between 0 and 1.</param>
/// <param name="b">The blue component ranging between 0 and 1.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb(float r, float g, float b)
: this(new Vector3(r, g, b))
: this(r, g, b, DefaultWorkingSpace)
{
}
@ -43,8 +64,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="g">The green component ranging between 0 and 1.</param>
/// <param name="b">The blue component ranging between 0 and 1.</param>
/// <param name="workingSpace">The rgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb(float r, float g, float b, RgbWorkingSpace workingSpace)
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb(float r, float g, float b, RgbWorkingSpaceBase workingSpace)
: this(new Vector3(r, g, b), workingSpace)
{
}
@ -53,7 +74,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="Rgb"/> struct.
/// </summary>
/// <param name="vector">The vector representing the r, g, b components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb(Vector3 vector)
: this(vector, DefaultWorkingSpace)
{
@ -64,68 +85,33 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <param name="vector">The vector representing the r, g, b components.</param>
/// <param name="workingSpace">The rgb working space.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgb(Vector3 vector, RgbWorkingSpace workingSpace)
: this()
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb(Vector3 vector, RgbWorkingSpaceBase workingSpace)
{
// Clamp to 0-1 range.
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One);
vector = Vector3.Clamp(vector, Min, Max);
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"/>
/// Allows the implicit conversion of an instance of <see cref="Rgb24"/> to a
/// <see cref="Rgb"/>.
/// </summary>
public RgbWorkingSpace WorkingSpace { get; }
/// <inheritdoc />
public Vector3 Vector => this.backingVector;
/// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param>
/// <returns>An instance of <see cref="Rgb"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static implicit operator Rgb(Rgb24 color) => new Rgb(color.R / 255F, color.G / 255F, color.B / 255F);
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Rgba32"/> to a
/// <see cref="Rgb"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Rgba32"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Rgb"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Rgb(Rgba32 color)
{
return new Rgb(color.R / 255F, color.G / 255F, color.B / 255F);
}
/// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param>
/// <returns>An instance of <see cref="Rgb"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static implicit operator Rgb(Rgba32 color) => new Rgb(color.R / 255F, color.G / 255F, color.B / 255F);
/// <summary>
/// Compares two <see cref="Rgb"/> objects for equality.
@ -139,66 +125,48 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <returns>
/// 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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
[MethodImpl(InliningOptions.ShortMethod)]
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(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "Rgb [ Empty ]"
: $"Rgb [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##} ]";
}
public override string ToString() => $"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})";
/// <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)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
}
}

42
src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.ColorSpaces
@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Chromaticity coordinates taken from:
/// <see href="http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html"/>
/// </summary>
internal static class RgbWorkingSpaces
public static class RgbWorkingSpaces
{
/// <summary>
/// sRgb working space.
@ -19,97 +19,97 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Uses proper companding function, according to:
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_Rgb_to_XYZ.html"/>
/// </remarks>
public static readonly RgbWorkingSpace SRgb = new RgbWorkingSpace(Illuminants.D65, new SRgbCompanding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
public static readonly RgbWorkingSpaceBase SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
/// <summary>
/// Simplified sRgb working space (uses <see cref="GammaCompanding">gamma companding</see> instead of <see cref="SRgbCompanding"/>).
/// See also <see cref="SRgb"/>.
/// </summary>
public static readonly RgbWorkingSpace SRgbSimplified = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
public static readonly RgbWorkingSpaceBase SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
/// <summary>
/// Rec. 709 (ITU-R Recommendation BT.709) working space.
/// </summary>
public static readonly RgbWorkingSpace Rec709 = new RgbWorkingSpace(Illuminants.D65, new Rec709Companding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F)));
public static readonly RgbWorkingSpaceBase Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F)));
/// <summary>
/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space.
/// </summary>
public static readonly RgbWorkingSpace Rec2020 = new RgbWorkingSpace(Illuminants.D65, new Rec2020Companding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F)));
public static readonly RgbWorkingSpaceBase Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F)));
/// <summary>
/// ECI Rgb v2 working space.
/// </summary>
public static readonly RgbWorkingSpace ECIRgbv2 = new RgbWorkingSpace(Illuminants.D50, new LCompanding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F)));
public static readonly RgbWorkingSpaceBase ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F)));
/// <summary>
/// Adobe Rgb (1998) working space.
/// </summary>
public static readonly RgbWorkingSpace AdobeRgb1998 = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
public static readonly RgbWorkingSpaceBase AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
/// <summary>
/// Apple sRgb working space.
/// </summary>
public static readonly RgbWorkingSpace ApplesRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F)));
public static readonly RgbWorkingSpaceBase ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F)));
/// <summary>
/// Best Rgb working space.
/// </summary>
public static readonly RgbWorkingSpace BestRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F)));
public static readonly RgbWorkingSpaceBase BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F)));
/// <summary>
/// Beta Rgb working space.
/// </summary>
public static readonly RgbWorkingSpace BetaRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F)));
public static readonly RgbWorkingSpaceBase BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F)));
/// <summary>
/// Bruce Rgb working space.
/// </summary>
public static readonly RgbWorkingSpace BruceRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
public static readonly RgbWorkingSpaceBase BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
/// <summary>
/// CIE Rgb working space.
/// </summary>
public static readonly RgbWorkingSpace CIERgb = new RgbWorkingSpace(Illuminants.E, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F)));
public static readonly RgbWorkingSpaceBase CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F)));
/// <summary>
/// ColorMatch Rgb working space.
/// </summary>
public static readonly RgbWorkingSpace ColorMatchRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F)));
public static readonly RgbWorkingSpaceBase ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F)));
/// <summary>
/// Don Rgb 4 working space.
/// </summary>
public static readonly RgbWorkingSpace DonRgb4 = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F)));
public static readonly RgbWorkingSpaceBase DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F)));
/// <summary>
/// Ekta Space PS5 working space.
/// </summary>
public static readonly RgbWorkingSpace EktaSpacePS5 = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F)));
public static readonly RgbWorkingSpaceBase EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F)));
/// <summary>
/// NTSC Rgb working space.
/// </summary>
public static readonly RgbWorkingSpace NTSCRgb = new RgbWorkingSpace(Illuminants.C, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F)));
public static readonly RgbWorkingSpaceBase NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F)));
/// <summary>
/// PAL/SECAM Rgb working space.
/// </summary>
public static readonly RgbWorkingSpace PALSECAMRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
public static readonly RgbWorkingSpaceBase PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
/// <summary>
/// ProPhoto Rgb working space.
/// </summary>
public static readonly RgbWorkingSpace ProPhotoRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F)));
public static readonly RgbWorkingSpaceBase ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F)));
/// <summary>
/// SMPTE-C Rgb working space.
/// </summary>
public static readonly RgbWorkingSpace SMPTECRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F)));
public static readonly RgbWorkingSpaceBase SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F)));
/// <summary>
/// Wide Gamut Rgb working space.
/// </summary>
public static readonly RgbWorkingSpace WideGamutRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F)));
public static readonly RgbWorkingSpaceBase WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F)));
}
}

127
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,17 +12,28 @@ 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>
public readonly struct YCbCr : IEquatable<YCbCr>
{
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = new Vector3(255);
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public readonly float Y;
/// <summary>
/// Vector which is used in clamping to the max value.
/// Gets the Cb chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
private static readonly Vector3 VectorMax = new Vector3(255F);
public readonly float Cb;
/// <summary>
/// The backing vector for SIMD support.
/// 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.
@ -31,7 +41,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="y">The y luminance component.</param>
/// <param name="cb">The cb chroma component.</param>
/// <param name="cr">The cr chroma component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public YCbCr(float y, float cb, float cr)
: this(new Vector3(y, cb, cr))
{
@ -41,117 +51,58 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Initializes a new instance of the <see cref="YCbCr"/> struct.
/// </summary>
/// <param name="vector">The vector representing the y, cb, cr components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public YCbCr(Vector3 vector)
{
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax);
}
/// <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;
vector = Vector3.Clamp(vector, Min, Max);
this.Y = vector.X;
this.Cb = vector.Y;
this.Cr = vector.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);
}
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(YCbCr left, YCbCr right) => !left.Equals(right);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public override string ToString()
{
return this.Equals(default)
? "YCbCr [ Empty ]"
: $"YCbCr [ Y={this.Y}, Cb={this.Cb}, Cr={this.Cr} ]";
}
public override string ToString() => $"YCbCr({this.Y}, {this.Cb}, {this.Cr})";
/// <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)]
[MethodImpl(InliningOptions.ShortMethod)]
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);
}
}
}

34
src/ImageSharp/Common/Extensions/ComparableExtensions.cs

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp
/// <returns>
/// The <see cref="byte"/> representing the clamped value.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static byte Clamp(this byte value, byte min, byte max)
{
// Order is important here as someone might set min to higher than max.
@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp
/// <returns>
/// The <see cref="int"/> representing the clamped value.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static uint Clamp(this uint value, uint min, uint max)
{
if (value >= max)
@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp
/// <returns>
/// The <see cref="int"/> representing the clamped value.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static int Clamp(this int value, int min, int max)
{
if (value >= max)
@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp
/// <returns>
/// The <see cref="float"/> representing the clamped value.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static float Clamp(this float value, float min, float max)
{
if (value >= max)
@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp
/// <returns>
/// The <see cref="double"/> representing the clamped value.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static double Clamp(this double value, double min, double max)
{
if (value >= max)
@ -136,27 +136,5 @@ namespace SixLabors.ImageSharp
return value;
}
/// <summary>
/// Converts an <see cref="float"/> to a <see cref="byte"/> first restricting the value between the
/// minimum and maximum allowable ranges.
/// </summary>
/// <param name="value">The <see cref="float"/> this method extends.</param>
/// <returns>The <see cref="byte"/></returns>
public static byte ToByte(this float value)
{
return (byte)value.Clamp(0, 255);
}
/// <summary>
/// Converts an <see cref="double"/> to a <see cref="byte"/> first restricting the value between the
/// minimum and maximum allowable ranges.
/// </summary>
/// <param name="value">The <see cref="double"/> this method extends.</param>
/// <returns>The <see cref="byte"/></returns>
public static byte ToByte(this double value)
{
return (byte)value.Clamp(0, 255);
}
}
}
}

78
src/ImageSharp/Common/Extensions/Vector4Extensions.cs

@ -5,7 +5,7 @@ using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
@ -55,8 +55,10 @@ namespace SixLabors.ImageSharp
for (int i = 0; i < vectors.Length; i++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
var s = new Vector4(v.W);
s.W = 1;
var s = new Vector4(v.W)
{
W = 1
};
v *= s;
}
}
@ -73,8 +75,10 @@ namespace SixLabors.ImageSharp
for (int i = 0; i < vectors.Length; i++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
var s = new Vector4(1 / v.W);
s.W = 1;
var s = new Vector4(1 / v.W)
{
W = 1
};
v *= s;
}
}
@ -90,7 +94,11 @@ namespace SixLabors.ImageSharp
public static Vector4 Compress(this Vector4 linear)
{
// TODO: Is there a faster way to do this?
return new Vector4(Compress(linear.X), Compress(linear.Y), Compress(linear.Z), linear.W);
return new Vector4(
SRgbCompanding.Compress(linear.X),
SRgbCompanding.Compress(linear.Y),
SRgbCompanding.Compress(linear.Z),
linear.W);
}
/// <summary>
@ -104,7 +112,11 @@ namespace SixLabors.ImageSharp
public static Vector4 Expand(this Vector4 gamma)
{
// TODO: Is there a faster way to do this?
return new Vector4(Expand(gamma.X), Expand(gamma.Y), Expand(gamma.Z), gamma.W);
return new Vector4(
SRgbCompanding.Expand(gamma.X),
SRgbCompanding.Expand(gamma.Y),
SRgbCompanding.Expand(gamma.Z),
gamma.W);
}
/// <summary>
@ -118,9 +130,9 @@ namespace SixLabors.ImageSharp
for (int i = 0; i < vectors.Length; i++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
v.X = Compress(v.X);
v.Y = Compress(v.Y);
v.Z = Compress(v.Z);
v.X = SRgbCompanding.Compress(v.X);
v.Y = SRgbCompanding.Compress(v.Y);
v.Z = SRgbCompanding.Compress(v.Z);
}
}
@ -135,50 +147,10 @@ namespace SixLabors.ImageSharp
for (int i = 0; i < vectors.Length; i++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
v.X = Expand(v.X);
v.Y = Expand(v.Y);
v.Z = Expand(v.Z);
}
}
/// <summary>
/// Gets the compressed sRGB value from an linear signal.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="signal">The signal value to compress.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(InliningOptions.ShortMethod)]
private static float Compress(float signal)
{
if (signal <= 0.0031308F)
{
return signal * 12.92F;
v.X = SRgbCompanding.Expand(v.X);
v.Y = SRgbCompanding.Expand(v.Y);
v.Z = SRgbCompanding.Expand(v.Z);
}
return (1.055F * MathF.Pow(signal, 0.41666666F)) - 0.055F;
}
/// <summary>
/// Gets the expanded linear value from an sRGB signal.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="signal">The signal value to expand.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(InliningOptions.ShortMethod)]
private static float Expand(float signal)
{
if (signal <= 0.04045F)
{
return signal / 12.92F;
}
return MathF.Pow((signal + 0.055F) / 1.055F, 2.4F);
}
}
}

44
src/ImageSharp/Common/Helpers/Guard.cs

@ -242,5 +242,49 @@ namespace SixLabors.ImageSharp
throw new ArgumentException($"Span-s must be at least of length {minLength}!", parameterName);
}
}
/// <summary>
/// Verifies that the given 'source' and 'dest' spans are at least of 'minLength' size.
/// Throwing an <see cref="ArgumentException"/> if the condition is not met.
/// </summary>
/// <typeparam name="TSource">The source element type</typeparam>
/// <typeparam name="TDest">The destination element type</typeparam>
/// <param name="source">The source span</param>
/// <param name="sourceParamName">The source parameter name</param>
/// <param name="dest">The destination span</param>
/// <param name="destParamName">The destination parameter name</param>
/// <param name="minLength">The minimum length</param>
public static void SpansMustBeSizedAtLeast<TSource, TDest>(
Span<TSource> source,
string sourceParamName,
Span<TDest> dest,
string destParamName,
int minLength)
{
MustBeSizedAtLeast(source, minLength, sourceParamName);
MustBeSizedAtLeast(dest, minLength, destParamName);
}
/// <summary>
/// Verifies that the given 'source' and 'dest' spans are at least of 'minLength' size.
/// Throwing an <see cref="ArgumentException"/> if the condition is not met.
/// </summary>
/// <typeparam name="TSource">The source element type</typeparam>
/// <typeparam name="TDest">The destination element type</typeparam>
/// <param name="source">The source span</param>
/// <param name="sourceParamName">The source parameter name</param>
/// <param name="dest">The destination span</param>
/// <param name="destParamName">The destination parameter name</param>
/// <param name="minLength">The minimum length</param>
public static void SpansMustBeSizedAtLeast<TSource, TDest>(
ReadOnlySpan<TSource> source,
string sourceParamName,
Span<TDest> dest,
string destParamName,
int minLength)
{
MustBeSizedAtLeast(source, minLength, sourceParamName);
MustBeSizedAtLeast(dest, minLength, destParamName);
}
}
}

22
src/ImageSharp/Common/Helpers/ImageMaths.cs

@ -53,6 +53,22 @@ namespace SixLabors.ImageSharp
return (x ^ y) - y;
}
/// <summary>
/// Returns a specified number raised to the power of 2
/// </summary>
/// <param name="x">A single-precision floating-point number</param>
/// <returns>The number <paramref name="x" /> raised to the power of 2.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Pow2(float x) => x * x;
/// <summary>
/// Returns a specified number raised to the power of 3
/// </summary>
/// <param name="x">A single-precision floating-point number</param>
/// <returns>The number <paramref name="x" /> raised to the power of 3.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Pow3(float x) => x * x * x;
/// <summary>
/// Returns how many bits are required to store the specified number of colors.
/// Performs a Log2() on the value.
@ -85,7 +101,7 @@ namespace SixLabors.ImageSharp
float denominator = MathF.Sqrt(2 * MathF.PI) * sigma;
float exponentNumerator = -x * x;
float exponentDenominator = (float)(2 * Math.Pow(sigma, 2));
float exponentDenominator = 2 * Pow2(sigma);
float left = Numerator / denominator;
float right = MathF.Exp(exponentNumerator / exponentDenominator);
@ -127,14 +143,12 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float GetBcValue(float x, float b, float c)
{
float temp;
if (x < 0F)
{
x = -x;
}
temp = x * x;
float temp = x * x;
if (x < 1F)
{
x = ((12 - (9 * b) - (6 * c)) * (x * temp)) + ((-18 + (12 * b) + (6 * c)) * temp) + (6 - (2 * b));

4
src/ImageSharp/Common/Helpers/InliningOptions.cs

@ -8,12 +8,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Global inlining options. Helps temporarily disable inling for better profiler output.
/// Global inlining options. Helps temporarily disable inlining for better profiler output.
/// </summary>
internal static class InliningOptions
{
#if PROFILING
public const MethodImplOptions ShortMethod = 0;
public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining;
#else
public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining;
#endif

4
src/ImageSharp/PixelFormats/README.md

@ -2,9 +2,5 @@
https://github.com/MonoGame/MonoGame
Rgba32 is our default format. As such it positioned within the ImageSharp root namespace to ensure visibility of the format.
All other pixel formats should be positioned within ImageSharp.PixelFormats to reduce intellisense burden.
The naming convention of each pixel format is to order the color components from least significant to most significant, reading from left to right.
For example in the Rgba32 pixel format the R component is the least significant byte, and the A component is the most significant.

15
src/ImageSharp/PixelFormats/Rgb24.cs

@ -50,6 +50,21 @@ namespace SixLabors.ImageSharp.PixelFormats
this.B = b;
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="ColorSpaces.Rgb"/> to a
/// <see cref="Rgb24"/>.
/// </summary>
/// <param name="color">The instance of <see cref="ColorSpaces.Rgb"/> to convert.</param>
/// <returns>An instance of <see cref="Rgb24"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Rgb24(ColorSpaces.Rgb color)
{
var vector = new Vector4(color.ToVector3(), 1);
Rgb24 rgb = default;
rgb.PackFromScaledVector4(vector);
return rgb;
}
/// <inheritdoc/>
public PixelOperations<Rgb24> CreatePixelOperations() => new PixelOperations<Rgb24>();

113
src/ImageSharp/PixelFormats/Rgba32.cs

@ -111,10 +111,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="a">The alpha component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgba32(float r, float g, float b, float a = 1)
: this()
{
this.Pack(r, g, b, a);
}
: this() => this.Pack(r, g, b, a);
/// <summary>
/// Initializes a new instance of the <see cref="Rgba32"/> struct.
@ -124,10 +121,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgba32(Vector3 vector)
: this()
{
this.Pack(ref vector);
}
: this() => this.Pack(ref vector);
/// <summary>
/// Initializes a new instance of the <see cref="Rgba32"/> struct.
@ -137,10 +131,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgba32(Vector4 vector)
: this()
{
this = PackNew(ref vector);
}
: this() => this = PackNew(ref vector);
/// <summary>
/// Initializes a new instance of the <see cref="Rgba32"/> struct.
@ -150,10 +141,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgba32(uint packed)
: this()
{
this.Rgba = packed;
}
: this() => this.Rgba = packed;
/// <summary>
/// Gets or sets the packed representation of the Rgba32 struct.
@ -206,23 +194,31 @@ namespace SixLabors.ImageSharp.PixelFormats
set => this.Rgba = value;
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="ColorSpaces.Rgb"/> to a
/// <see cref="Rgba32"/>.
/// </summary>
/// <param name="color">The instance of <see cref="ColorSpaces.Rgb"/> to convert.</param>
/// <returns>An instance of <see cref="Rgba32"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Rgba32(ColorSpaces.Rgb color)
{
var vector = new Vector4(color.ToVector3(), 1);
Rgba32 rgba = default;
rgba.PackFromScaledVector4(vector);
return rgba;
}
/// <summary>
/// Compares two <see cref="Rgba32"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgba32"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgba32"/> on the right side of the operand.
/// </param>
/// <param name="left">The <see cref="Rgba32"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Rgba32"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Rgba32 left, Rgba32 right)
{
return left.Rgba == right.Rgba;
}
public static bool operator ==(Rgba32 left, Rgba32 right) => left.Rgba == right.Rgba;
/// <summary>
/// Compares two <see cref="Rgba32"/> objects for equality.
@ -233,10 +229,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Rgba32 left, Rgba32 right)
{
return left.Rgba != right.Rgba;
}
public static bool operator !=(Rgba32 left, Rgba32 right) => left.Rgba != right.Rgba;
/// <summary>
/// Creates a new instance of the <see cref="Rgba32"/> struct.
@ -248,20 +241,14 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <returns>
/// The <see cref="Rgba32"/>.
/// </returns>
public static Rgba32 FromHex(string hex)
{
return ColorBuilder<Rgba32>.FromHex(hex);
}
public static Rgba32 FromHex(string hex) => ColorBuilder<Rgba32>.FromHex(hex);
/// <inheritdoc />
public PixelOperations<Rgba32> CreatePixelOperations() => new PixelOperations();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba32(Rgba32 source)
{
this = source;
}
public void PackFromRgba32(Rgba32 source) => this = source;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -295,17 +282,11 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
dest = Unsafe.As<Rgba32, Rgb24>(ref this);
}
public void ToRgb24(ref Rgb24 dest) => dest = Unsafe.As<Rgba32, Rgb24>(ref this);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
dest = this;
}
public void ToRgba32(ref Rgba32 dest) => dest = this;
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -338,31 +319,19 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
public void PackFromScaledVector4(Vector4 vector) => this.PackFromVector4(vector);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
public Vector4 ToScaledVector4() => this.ToVector4();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
{
this.Pack(ref vector);
}
public void PackFromVector4(Vector4 vector) => this.Pack(ref vector);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
{
return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes;
}
public Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes;
/// <summary>
/// Gets the value of this struct as <see cref="Bgra32"/>.
@ -417,23 +386,14 @@ namespace SixLabors.ImageSharp.PixelFormats
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is Rgba32 rgba32 && this.Equals(rgba32);
}
public override bool Equals(object obj) => obj is Rgba32 rgba32 && this.Equals(rgba32);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Rgba32 other)
{
return this.Rgba == other.Rgba;
}
public bool Equals(Rgba32 other) => this.Rgba == other.Rgba;
/// <inheritdoc/>
public override string ToString()
{
return $"({this.R},{this.G},{this.B},{this.A})";
}
public override string ToString() => $"({this.R},{this.G},{this.B},{this.A})";
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -444,10 +404,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// </summary>
/// <returns>A <see cref="Vector4"/> of values in [0, 255] </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Vector4 ToByteScaledVector4()
{
return new Vector4(this.R, this.G, this.B, this.A);
}
internal Vector4 ToByteScaledVector4() => new Vector4(this.R, this.G, this.B, this.A);
/// <summary>
/// Packs a <see cref="Vector4"/> into a color returning a new instance as a result.

2
tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs

@ -14,7 +14,7 @@
private static readonly RGBColor RGBColor = new RGBColor(0.206162, 0.260277, 0.746717, RGBWorkingSpaces.WideGamutRGB);
private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb };
private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb });
private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter { TargetRGBWorkingSpace = RGBWorkingSpaces.sRGB };

40
tests/ImageSharp.Benchmarks/General/Pow.cs

@ -0,0 +1,40 @@
using System;
using BenchmarkDotNet.Attributes;
namespace SixLabors.ImageSharp.Benchmarks.General
{
public class Pow
{
[Params(-1.333F, 1.333F)]
public float X { get; set; }
[Benchmark(Baseline = true, Description = "Math.Pow 2")]
public float MathPow()
{
float x = this.X;
return (float)Math.Pow(x, 2);
}
[Benchmark(Description = "Pow x2")]
public float PowMult()
{
float x = this.X;
return x * x;
}
[Benchmark(Description = "Math.Pow 3")]
public float MathPow3()
{
float x = this.X;
return (float)Math.Pow(x, 3);
}
[Benchmark(Description = "Pow x3")]
public float PowMult3()
{
float x = this.X;
return x * x * x;
}
}
}

3
tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj

@ -15,13 +15,12 @@
<Compile Include="..\ImageSharp.Tests\TestImages.cs" Link="Tests\TestImages.cs" />
<Compile Include="..\ImageSharp.Tests\TestUtilities\TestEnvironment.cs" Link="Tests\TestEnvironment.cs" />
</ItemGroup>
<ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.10.14" />
<PackageReference Include="Colourful" Version="2.0.0" />
<PackageReference Include="SixLabors.Shapes.Text" Version="1.0.0-beta0007" />
<PackageReference Include="System.Drawing.Common" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ImageSharp.Drawing\ImageSharp.Drawing.csproj" />
<ProjectReference Include="..\..\src\ImageSharp\ImageSharp.csproj" />

46
tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs

@ -0,0 +1,46 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces
{
/// <summary>
/// Tests the <see cref="CieLab"/> struct.
/// </summary>
public class CieLabTests
{
[Fact]
public void CieLabConstructorAssignsFields()
{
const float l = 75F;
const float a = -64F;
const float b = 87F;
var cieLab = new CieLab(l, a, b);
Assert.Equal(l, cieLab.L);
Assert.Equal(a, cieLab.A);
Assert.Equal(b, cieLab.B);
}
[Fact]
public void CieLabEquality()
{
var x = default(CieLab);
var y = new CieLab(Vector3.One);
Assert.True(default(CieLab) == default(CieLab));
Assert.True(default(CieLab) != new CieLab(1, 0, 1));
Assert.False(default(CieLab) == new CieLab(1, 0, 1));
Assert.Equal(default(CieLab), default(CieLab));
Assert.Equal(new CieLab(1, 0, 1), new CieLab(1, 0, 1));
Assert.Equal(new CieLab(Vector3.One), new CieLab(Vector3.One));
Assert.False(x.Equals(y));
Assert.False(default(CieLab) == new CieLab(1, 0, 1));
Assert.False(x.Equals((object)y));
Assert.False(x.GetHashCode().Equals(y.GetHashCode()));
}
}
}

44
tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs

@ -0,0 +1,44 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces
{
/// <summary>
/// Tests the <see cref="CieLch"/> struct.
/// </summary>
public class CieLchTests
{
[Fact]
public void CieLchConstructorAssignsFields()
{
const float l = 75F;
const float c = 64F;
const float h = 287F;
var cieLch = new CieLch(l, c, h);
Assert.Equal(l, cieLch.L);
Assert.Equal(c, cieLch.C);
Assert.Equal(h, cieLch.H);
}
[Fact]
public void CieLchEquality()
{
var x = default(CieLch);
var y = new CieLch(Vector3.One);
Assert.True(default(CieLch) == default(CieLch));
Assert.False(default(CieLch) != default(CieLch));
Assert.Equal(default(CieLch), default(CieLch));
Assert.Equal(new CieLch(1, 0, 1), new CieLch(1, 0, 1));
Assert.Equal(new CieLch(Vector3.One), new CieLch(Vector3.One));
Assert.False(x.Equals(y));
Assert.False(x.Equals((object)y));
Assert.False(x.GetHashCode().Equals(y.GetHashCode()));
}
}
}

44
tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs

@ -0,0 +1,44 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces
{
/// <summary>
/// Tests the <see cref="CieLchuv"/> struct.
/// </summary>
public class CieLchuvTests
{
[Fact]
public void CieLchuvConstructorAssignsFields()
{
const float l = 75F;
const float c = 64F;
const float h = 287F;
var cieLchuv = new CieLchuv(l, c, h);
Assert.Equal(l, cieLchuv.L);
Assert.Equal(c, cieLchuv.C);
Assert.Equal(h, cieLchuv.H);
}
[Fact]
public void CieLchuvEquality()
{
var x = default(CieLchuv);
var y = new CieLchuv(Vector3.One);
Assert.True(default(CieLchuv) == default(CieLchuv));
Assert.False(default(CieLchuv) != default(CieLchuv));
Assert.Equal(default(CieLchuv), default(CieLchuv));
Assert.Equal(new CieLchuv(1, 0, 1), new CieLchuv(1, 0, 1));
Assert.Equal(new CieLchuv(Vector3.One), new CieLchuv(Vector3.One));
Assert.False(x.Equals(y));
Assert.False(x.Equals((object)y));
Assert.False(x.GetHashCode().Equals(y.GetHashCode()));
}
}
}

44
tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs

@ -0,0 +1,44 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces
{
/// <summary>
/// Tests the <see cref="CieLuv"/> struct.
/// </summary>
public class CieLuvTests
{
[Fact]
public void CieLuvConstructorAssignsFields()
{
const float l = 75F;
const float c = -64F;
const float h = 87F;
var cieLuv = new CieLuv(l, c, h);
Assert.Equal(l, cieLuv.L);
Assert.Equal(c, cieLuv.U);
Assert.Equal(h, cieLuv.V);
}
[Fact]
public void CieLuvEquality()
{
var x = default(CieLuv);
var y = new CieLuv(Vector3.One);
Assert.True(default(CieLuv) == default(CieLuv));
Assert.False(default(CieLuv) != default(CieLuv));
Assert.Equal(default(CieLuv), default(CieLuv));
Assert.Equal(new CieLuv(1, 0, 1), new CieLuv(1, 0, 1));
Assert.Equal(new CieLuv(Vector3.One), new CieLuv(Vector3.One));
Assert.False(x.Equals(y));
Assert.False(x.Equals((object)y));
Assert.False(x.GetHashCode().Equals(y.GetHashCode()));
}
}
}

43
tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs

@ -0,0 +1,43 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces
{
/// <summary>
/// Tests the <see cref="CieXyChromaticityCoordinates"/> struct.
/// </summary>
public class CieXyChromaticityCoordinatesTests
{
[Fact]
public void CieXyChromaticityCoordinatesConstructorAssignsFields()
{
const float x = .75F;
const float y = .64F;
var coordinates = new CieXyChromaticityCoordinates(x, y);
Assert.Equal(x, coordinates.X);
Assert.Equal(y, coordinates.Y);
}
[Fact]
public void CieXyChromaticityCoordinatesEquality()
{
var x = default(CieXyChromaticityCoordinates);
var y = new CieXyChromaticityCoordinates(1, 1);
Assert.True(default(CieXyChromaticityCoordinates) == default(CieXyChromaticityCoordinates));
Assert.True(default(CieXyChromaticityCoordinates) != new CieXyChromaticityCoordinates(1, 0));
Assert.False(default(CieXyChromaticityCoordinates) == new CieXyChromaticityCoordinates(1, 0));
Assert.Equal(default(CieXyChromaticityCoordinates), default(CieXyChromaticityCoordinates));
Assert.Equal(new CieXyChromaticityCoordinates(1, 0), new CieXyChromaticityCoordinates(1, 0));
Assert.Equal(new CieXyChromaticityCoordinates(1, 1), new CieXyChromaticityCoordinates(1, 1));
Assert.False(x.Equals(y));
Assert.False(default(CieXyChromaticityCoordinates) == new CieXyChromaticityCoordinates(1, 0));
Assert.False(x.Equals((object)y));
Assert.False(x.GetHashCode().Equals(y.GetHashCode()));
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save