Browse Source

Update ColorProfileConverter to handle ICCProfiles

pull/1567/head
James Jackson-South 1 year ago
parent
commit
441f07e57d
  1. 46
      src/ImageSharp/ColorProfiles/CieLab.cs
  2. 46
      src/ImageSharp/ColorProfiles/CieLch.cs
  3. 46
      src/ImageSharp/ColorProfiles/CieLchuv.cs
  4. 14
      src/ImageSharp/ColorProfiles/CieLuv.cs
  5. 33
      src/ImageSharp/ColorProfiles/CieXyy.cs
  6. 42
      src/ImageSharp/ColorProfiles/CieXyz.cs
  7. 26
      src/ImageSharp/ColorProfiles/Cmyk.cs
  8. 15
      src/ImageSharp/ColorProfiles/ColorConversionOptions.cs
  9. 7
      src/ImageSharp/ColorProfiles/ColorProfileConverter.cs
  10. 11
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs
  11. 11
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs
  12. 11
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs
  13. 11
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs
  14. 11
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs
  15. 11
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs
  16. 256
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsIcc.cs
  17. 11
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs
  18. 11
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs
  19. 11
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs
  20. 33
      src/ImageSharp/ColorProfiles/Hsl.cs
  21. 33
      src/ImageSharp/ColorProfiles/Hsv.cs
  22. 46
      src/ImageSharp/ColorProfiles/HunterLab.cs
  23. 46
      src/ImageSharp/ColorProfiles/IColorProfile.cs
  24. 15
      src/ImageSharp/ColorProfiles/Icc/IccConverterbase.cs
  25. 2
      src/ImageSharp/ColorProfiles/Icc/IccProfileConverter.cs
  26. 44
      src/ImageSharp/ColorProfiles/Lms.cs
  27. 97
      src/ImageSharp/ColorProfiles/Rgb.cs
  28. 42
      src/ImageSharp/ColorProfiles/YCbCr.cs
  29. 1
      src/ImageSharp/Formats/DecoderOptions.cs
  30. 3
      src/ImageSharp/PixelFormats/IPixel.cs
  31. 3
      src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs
  32. 2
      src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs
  33. 2
      tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs
  34. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs
  35. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs
  36. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs
  37. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs
  38. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs
  39. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs
  40. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs
  41. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs
  42. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs
  43. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs
  44. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs
  45. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs
  46. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs
  47. 4
      tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs
  48. 4
      tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs
  49. 4
      tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs
  50. 16
      tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs
  51. 4
      tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/LutEntryCalculatorTests.cs
  52. 8
      tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs
  53. 2
      tests/ImageSharp.Tests/ColorProfiles/VonKriesChromaticAdaptationTests.cs

46
src/ImageSharp/ColorProfiles/CieLab.cs

@ -4,6 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.ColorProfiles;
@ -82,6 +83,49 @@ public readonly struct CieLab : IProfileConnectingSpace<CieLab, CieXyz>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right);
/// <inheritdoc/>
public Vector4 ToScaledVector4()
{
Vector3 v3 = default;
v3 += this.AsVector3Unsafe();
v3 += new Vector3(0, 128F, 128F);
v3 /= new Vector3(100F, 255F, 255F);
return new Vector4(v3, 1F);
}
/// <inheritdoc/>
public static CieLab FromScaledVector4(Vector4 source)
{
Vector3 v3 = source.AsVector128().AsVector3();
v3 *= new Vector3(100F, 255, 255);
v3 -= new Vector3(0, 128F, 128F);
return new CieLab(v3);
}
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<CieLab> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i].ToScaledVector4();
}
}
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<CieLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = FromScaledVector4(source[i]);
}
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static CieLab FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
@ -136,7 +180,7 @@ public readonly struct CieLab : IProfileConnectingSpace<CieLab, CieXyz>
float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? Numerics.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa;
float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa;
CieXyz whitePoint = options.WhitePoint;
CieXyz whitePoint = options.SourceWhitePoint;
Vector3 wxyz = new(whitePoint.X, whitePoint.Y, whitePoint.Z);
Vector3 xyzr = new(xr, yr, zr);

46
src/ImageSharp/ColorProfiles/CieLch.cs

@ -4,6 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.ColorProfiles;
@ -50,7 +51,7 @@ public readonly struct CieLch : IColorProfile<CieLch, CieLab>
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 200.</remarks>
/// <remarks>A value ranging from -200 to 200.</remarks>
/// </summary>
public float C { get; }
@ -82,6 +83,49 @@ public readonly struct CieLch : IColorProfile<CieLch, CieLab>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieLch left, CieLch right) => !left.Equals(right);
/// <inheritdoc/>
public Vector4 ToScaledVector4()
{
Vector3 v3 = default;
v3 += this.AsVector3Unsafe();
v3 += new Vector3(0, 200, 0);
v3 /= new Vector3(100, 400, 360);
return new Vector4(v3, 1F);
}
/// <inheritdoc/>
public static CieLch FromScaledVector4(Vector4 source)
{
Vector3 v3 = source.AsVector128().AsVector3();
v3 *= new Vector3(100, 400, 360);
v3 -= new Vector3(0, 200, 0);
return new CieLch(v3);
}
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<CieLch> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i].ToScaledVector4();
}
}
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<CieLch> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = FromScaledVector4(source[i]);
}
}
/// <inheritdoc/>
public static CieLch FromProfileConnectingSpace(ColorConversionOptions options, in CieLab source)
{

46
src/ImageSharp/ColorProfiles/CieLchuv.cs

@ -4,6 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.ColorProfiles;
@ -51,7 +52,7 @@ public readonly struct CieLchuv : IColorProfile<CieLchuv, CieXyz>
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 200.</remarks>
/// <remarks>A value ranging from -200 to 200.</remarks>
/// </summary>
public float C { get; }
@ -81,6 +82,49 @@ public readonly struct CieLchuv : IColorProfile<CieLchuv, CieXyz>
/// </returns>
public static bool operator !=(CieLchuv left, CieLchuv right) => !left.Equals(right);
/// <inheritdoc/>
public Vector4 ToScaledVector4()
{
Vector3 v3 = default;
v3 += this.AsVector3Unsafe();
v3 += new Vector3(0, 200, 0);
v3 /= new Vector3(100, 400, 360);
return new Vector4(v3, 1F);
}
/// <inheritdoc/>
public static CieLchuv FromScaledVector4(Vector4 source)
{
Vector3 v3 = source.AsVector128().AsVector3();
v3 *= new Vector3(100, 400, 360);
v3 -= new Vector3(0, 200, 0);
return new CieLchuv(v3);
}
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<CieLchuv> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i].ToScaledVector4();
}
}
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<CieLchuv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = FromScaledVector4(source[i]);
}
}
/// <inheritdoc/>
public static CieLchuv FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
{

14
src/ImageSharp/ColorProfiles/CieLuv.cs

@ -84,6 +84,18 @@ public readonly struct CieLuv : IColorProfile<CieLuv, CieXyz>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieLuv left, CieLuv right) => !left.Equals(right);
/// <inheritdoc/>
public Vector4 ToScaledVector4() => throw new NotImplementedException();
/// <inheritdoc/>
public static CieLuv FromScaledVector4(Vector4 source) => throw new NotImplementedException();
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<CieLuv> source, Span<Vector4> destination) => throw new NotImplementedException();
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<CieLuv> destination) => throw new NotImplementedException();
/// <inheritdoc/>
public static CieLuv FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
{
@ -143,7 +155,7 @@ public readonly struct CieLuv : IColorProfile<CieLuv, CieXyz>
// Use doubles here for accuracy.
// Conversion algorithm described here:
// http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html
CieXyz whitePoint = options.WhitePoint;
CieXyz whitePoint = options.SourceWhitePoint;
double l = this.L, u = this.U, v = this.V;

33
src/ImageSharp/ColorProfiles/CieXyy.cs

@ -4,6 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.ColorProfiles;
@ -83,6 +84,38 @@ public readonly struct CieXyy : IColorProfile<CieXyy, CieXyz>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right);
/// <inheritdoc/>
public Vector4 ToScaledVector4()
=> new(this.AsVector3Unsafe(), 1F);
/// <inheritdoc/>
public static CieXyy FromScaledVector4(Vector4 source)
=> new(source.AsVector128().AsVector3());
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<CieXyy> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i].ToScaledVector4();
}
}
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = FromScaledVector4(source[i]);
}
}
/// <inheritdoc/>
public static CieXyy FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
{

42
src/ImageSharp/ColorProfiles/CieXyz.cs

@ -4,6 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.ColorProfiles;
@ -88,6 +89,47 @@ public readonly struct CieXyz : IProfileConnectingSpace<CieXyz, CieXyz>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 ToVector3() => new(this.X, this.Y, this.Z);
/// <inheritdoc/>
public Vector4 ToScaledVector4()
{
Vector3 v3 = default;
v3 += this.AsVector3Unsafe();
v3 *= 32768F / 65535;
return new Vector4(v3, 1F);
}
/// <inheritdoc/>
public static CieXyz FromScaledVector4(Vector4 source)
{
Vector3 v3 = source.AsVector128().AsVector3();
v3 *= 65535 / 32768F;
return new CieXyz(v3);
}
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<CieXyz> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i].ToScaledVector4();
}
}
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = FromScaledVector4(source[i]);
}
}
/// <inheritdoc/>
public static CieXyz FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
=> new(source.X, source.Y, source.Z);

26
src/ImageSharp/ColorProfiles/Cmyk.cs

@ -89,6 +89,32 @@ public readonly struct Cmyk : IColorProfile<Cmyk, Rgb>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Cmyk left, Cmyk right) => !left.Equals(right);
/// <inheritdoc/>
public Vector4 ToScaledVector4()
{
Vector4 v4 = default;
v4 += this.AsVector4Unsafe();
return v4;
}
/// <inheritdoc/>
public static Cmyk FromScaledVector4(Vector4 source)
=> new(source);
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<Cmyk> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
MemoryMarshal.Cast<Cmyk, Vector4>(source).CopyTo(destination);
}
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
MemoryMarshal.Cast<Vector4, Cmyk>(source).CopyTo(destination);
}
/// <inheritdoc/>
public static Cmyk FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source)
{

15
src/ImageSharp/ColorProfiles/ColorConversionOptions.cs

@ -4,6 +4,7 @@
using System.Numerics;
using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorProfiles;
@ -27,7 +28,7 @@ public class ColorConversionOptions
/// <summary>
/// Gets the source white point used for chromatic adaptation in conversions from/to XYZ color space.
/// </summary>
public CieXyz WhitePoint { get; init; } = KnownIlluminants.D50;
public CieXyz SourceWhitePoint { get; init; } = KnownIlluminants.D50;
/// <summary>
/// Gets the destination white point used for chromatic adaptation in conversions from/to XYZ color space.
@ -37,13 +38,23 @@ public class ColorConversionOptions
/// <summary>
/// Gets the source working space used for companding in conversions from/to XYZ color space.
/// </summary>
public RgbWorkingSpace RgbWorkingSpace { get; init; } = KnownRgbWorkingSpaces.SRgb;
public RgbWorkingSpace SourceRgbWorkingSpace { get; init; } = KnownRgbWorkingSpaces.SRgb;
/// <summary>
/// Gets the destination working space used for companding in conversions from/to XYZ color space.
/// </summary>
public RgbWorkingSpace TargetRgbWorkingSpace { get; init; } = KnownRgbWorkingSpaces.SRgb;
/// <summary>
/// Gets the source ICC profile.
/// </summary>
public IccProfile? SourceIccProfile { get; init; }
/// <summary>
/// Gets the target ICC profile.
/// </summary>
public IccProfile? TargetIccProfile { get; init; }
/// <summary>
/// Gets the transformation matrix used in conversion to perform chromatic adaptation.
/// <see cref="KnownChromaticAdaptationMatrices"/> for further information. Default is Bradford.

7
src/ImageSharp/ColorProfiles/ColorProfileConverter.cs

@ -33,8 +33,8 @@ public class ColorProfileConverter
where TTo : struct, IColorProfile
{
CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint
? this.Options.WhitePoint
: this.Options.RgbWorkingSpace.WhitePoint;
? this.Options.SourceWhitePoint
: this.Options.SourceRgbWorkingSpace.WhitePoint;
CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint
? this.Options.TargetWhitePoint
@ -42,4 +42,7 @@ public class ColorProfileConverter
return (sourceWhitePoint, targetWhitePoint);
}
internal bool ShouldUseIccProfiles()
=> this.Options.SourceIccProfile != null && this.Options.TargetIccProfile != null;
}

11
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs

@ -12,6 +12,11 @@ internal static class ColorProfileConverterExtensionsCieLabCieLab
where TFrom : struct, IColorProfile<TFrom, CieLab>
where TTo : struct, IColorProfile<TTo, CieLab>
{
if (converter.ShouldUseIccProfiles())
{
return converter.ConvertUsingIccProfile<TFrom, TTo>(source);
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS
@ -33,6 +38,12 @@ internal static class ColorProfileConverterExtensionsCieLabCieLab
where TFrom : struct, IColorProfile<TFrom, CieLab>
where TTo : struct, IColorProfile<TTo, CieLab>
{
if (converter.ShouldUseIccProfiles())
{
converter.ConvertUsingIccProfile(source, destination);
return;
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS.

11
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs

@ -12,6 +12,11 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz
where TFrom : struct, IColorProfile<TFrom, CieLab>
where TTo : struct, IColorProfile<TTo, CieXyz>
{
if (converter.ShouldUseIccProfiles())
{
return converter.ConvertUsingIccProfile<TFrom, TTo>(source);
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS
@ -32,6 +37,12 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz
where TFrom : struct, IColorProfile<TFrom, CieLab>
where TTo : struct, IColorProfile<TTo, CieXyz>
{
if (converter.ShouldUseIccProfiles())
{
converter.ConvertUsingIccProfile(source, destination);
return;
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS.

11
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs

@ -12,6 +12,11 @@ internal static class ColorProfileConverterExtensionsCieLabRgb
where TFrom : struct, IColorProfile<TFrom, CieLab>
where TTo : struct, IColorProfile<TTo, Rgb>
{
if (converter.ShouldUseIccProfiles())
{
return converter.ConvertUsingIccProfile<TFrom, TTo>(source);
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS
@ -33,6 +38,12 @@ internal static class ColorProfileConverterExtensionsCieLabRgb
where TFrom : struct, IColorProfile<TFrom, CieLab>
where TTo : struct, IColorProfile<TTo, Rgb>
{
if (converter.ShouldUseIccProfiles())
{
converter.ConvertUsingIccProfile(source, destination);
return;
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS.

11
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs

@ -12,6 +12,11 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab
where TFrom : struct, IColorProfile<TFrom, CieXyz>
where TTo : struct, IColorProfile<TTo, CieLab>
{
if (converter.ShouldUseIccProfiles())
{
return converter.ConvertUsingIccProfile<TFrom, TTo>(source);
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS
@ -32,6 +37,12 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab
where TFrom : struct, IColorProfile<TFrom, CieXyz>
where TTo : struct, IColorProfile<TTo, CieLab>
{
if (converter.ShouldUseIccProfiles())
{
converter.ConvertUsingIccProfile(source, destination);
return;
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS.

11
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs

@ -12,6 +12,11 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz
where TFrom : struct, IColorProfile<TFrom, CieXyz>
where TTo : struct, IColorProfile<TTo, CieXyz>
{
if (converter.ShouldUseIccProfiles())
{
return converter.ConvertUsingIccProfile<TFrom, TTo>(source);
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS
@ -29,6 +34,12 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz
where TFrom : struct, IColorProfile<TFrom, CieXyz>
where TTo : struct, IColorProfile<TTo, CieXyz>
{
if (converter.ShouldUseIccProfiles())
{
converter.ConvertUsingIccProfile(source, destination);
return;
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS.

11
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs

@ -12,6 +12,11 @@ internal static class ColorProfileConverterExtensionsCieXyzRgb
where TFrom : struct, IColorProfile<TFrom, CieXyz>
where TTo : struct, IColorProfile<TTo, Rgb>
{
if (converter.ShouldUseIccProfiles())
{
return converter.ConvertUsingIccProfile<TFrom, TTo>(source);
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS
@ -32,6 +37,12 @@ internal static class ColorProfileConverterExtensionsCieXyzRgb
where TFrom : struct, IColorProfile<TFrom, CieXyz>
where TTo : struct, IColorProfile<TTo, Rgb>
{
if (converter.ShouldUseIccProfiles())
{
converter.ConvertUsingIccProfile(source, destination);
return;
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS.

256
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsIcc.cs

@ -0,0 +1,256 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorProfiles.Icc;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorProfiles;
internal static class ColorProfileConverterExtensionsIcc
{
internal static TTo ConvertUsingIccProfile<TFrom, TTo>(this ColorProfileConverter converter, in TFrom source)
where TFrom : struct, IColorProfile<TFrom>
where TTo : struct, IColorProfile<TTo>
{
// TODO: Validation of ICC Profiles against color profile. Is this possible?
if (converter.Options.SourceIccProfile is null)
{
throw new InvalidOperationException("Source ICC profile is missing.");
}
if (converter.Options.TargetIccProfile is null)
{
throw new InvalidOperationException("Target ICC profile is missing.");
}
ColorProfileConverter pcsConverter = new(new ColorConversionOptions()
{
MemoryAllocator = converter.Options.MemoryAllocator,
// TODO: Double check this but I think these are normalized values.
SourceWhitePoint = CieXyz.FromScaledVector4(new(converter.Options.SourceIccProfile.Header.PcsIlluminant, 1F)),
TargetWhitePoint = CieXyz.FromScaledVector4(new(converter.Options.TargetIccProfile.Header.PcsIlluminant, 1F)),
});
IccDataToPcsConverter sourceConverter = new(converter.Options.SourceIccProfile);
IccPcsToDataConverter targetConverter = new(converter.Options.TargetIccProfile);
IccColorSpaceType sourcePcsType = converter.Options.SourceIccProfile.Header.ProfileConnectionSpace;
IccColorSpaceType targetPcsType = converter.Options.TargetIccProfile.Header.ProfileConnectionSpace;
IccVersion sourceVersion = converter.Options.SourceIccProfile.Header.Version;
IccVersion targetVersion = converter.Options.TargetIccProfile.Header.Version;
Vector4 pcs = sourceConverter.Calculate(source.ToScaledVector4());
// Profile connecting spaces can only be Lab, XYZ.
if (sourcePcsType is IccColorSpaceType.CieLab && targetPcsType is IccColorSpaceType.CieXyz)
{
// Convert from Lab to XYZ.
CieLab lab = CieLab.FromScaledVector4(pcs);
CieXyz xyz = pcsConverter.Convert<CieLab, CieXyz>(in lab);
pcs = xyz.ToScaledVector4();
}
else if (sourcePcsType is IccColorSpaceType.CieXyz && targetPcsType is IccColorSpaceType.CieLab)
{
// Convert from XYZ to Lab.
CieXyz xyz = CieXyz.FromScaledVector4(pcs);
CieLab lab = pcsConverter.Convert<CieXyz, CieLab>(in xyz);
pcs = lab.ToScaledVector4();
}
else if (sourcePcsType is IccColorSpaceType.CieXyz && targetPcsType is IccColorSpaceType.CieXyz)
{
// Convert from XYZ to XYZ.
CieXyz xyz = CieXyz.FromScaledVector4(pcs);
CieXyz targetXyz = pcsConverter.Convert<CieXyz, CieXyz>(in xyz);
pcs = targetXyz.ToScaledVector4();
}
else if (sourcePcsType is IccColorSpaceType.CieLab && targetPcsType is IccColorSpaceType.CieLab)
{
// Convert from Lab to Lab.
if (sourceVersion.Major == 4 && targetVersion.Major == 2)
{
// Convert from Lab v4 to Lab v2.
pcs = LabToLabV2(pcs);
}
else if (sourceVersion.Major == 2 && targetVersion.Major == 4)
{
// Convert from Lab v2 to Lab v4.
pcs = LabV2ToLab(pcs);
}
CieLab lab = CieLab.FromScaledVector4(pcs);
CieLab targetLab = pcsConverter.Convert<CieLab, CieLab>(in lab);
pcs = targetLab.ToScaledVector4();
}
// Convert to the target space.
return TTo.FromScaledVector4(targetConverter.Calculate(pcs));
}
internal static void ConvertUsingIccProfile<TFrom, TTo>(this ColorProfileConverter converter, ReadOnlySpan<TFrom> source, Span<TTo> destination)
where TFrom : struct, IColorProfile<TFrom>
where TTo : struct, IColorProfile<TTo>
{
// TODO: Validation of ICC Profiles against color profile. Is this possible?
if (converter.Options.SourceIccProfile is null)
{
throw new InvalidOperationException("Source ICC profile is missing.");
}
if (converter.Options.TargetIccProfile is null)
{
throw new InvalidOperationException("Target ICC profile is missing.");
}
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(destination));
ColorProfileConverter pcsConverter = new(new ColorConversionOptions()
{
MemoryAllocator = converter.Options.MemoryAllocator,
// TODO: Double check this but I think these are normalized values.
SourceWhitePoint = CieXyz.FromScaledVector4(new(converter.Options.SourceIccProfile.Header.PcsIlluminant, 1F)),
TargetWhitePoint = CieXyz.FromScaledVector4(new(converter.Options.TargetIccProfile.Header.PcsIlluminant, 1F)),
});
IccDataToPcsConverter sourceConverter = new(converter.Options.SourceIccProfile);
IccPcsToDataConverter targetConverter = new(converter.Options.TargetIccProfile);
IccColorSpaceType sourcePcsType = converter.Options.SourceIccProfile.Header.ProfileConnectionSpace;
IccColorSpaceType targetPcsType = converter.Options.TargetIccProfile.Header.ProfileConnectionSpace;
IccVersion sourceVersion = converter.Options.SourceIccProfile.Header.Version;
IccVersion targetVersion = converter.Options.TargetIccProfile.Header.Version;
using IMemoryOwner<Vector4> pcsBuffer = converter.Options.MemoryAllocator.Allocate<Vector4>(source.Length);
Span<Vector4> pcsNormalized = pcsBuffer.GetSpan();
// First normalize the values.
TFrom.ToScaledVector4(source, pcsNormalized);
// Now convert to the PCS space.
sourceConverter.Calculate(pcsNormalized, pcsNormalized);
// Profile connecting spaces can only be Lab, XYZ.
if (sourcePcsType is IccColorSpaceType.CieLab && targetPcsType is IccColorSpaceType.CieXyz)
{
// Convert from Lab to XYZ.
using IMemoryOwner<CieLab> pcsFromBuffer = converter.Options.MemoryAllocator.Allocate<CieLab>(source.Length);
Span<CieLab> pcsFrom = pcsFromBuffer.GetSpan();
CieLab.FromScaledVector4(pcsNormalized, pcsFrom);
using IMemoryOwner<CieXyz> pcsToBuffer = converter.Options.MemoryAllocator.Allocate<CieXyz>(source.Length);
Span<CieXyz> pcsTo = pcsToBuffer.GetSpan();
pcsConverter.Convert<CieLab, CieXyz>(pcsFrom, pcsTo);
// Convert to the target normalized PCS space.
CieXyz.ToScaledVector4(pcsTo, pcsNormalized);
}
else if (sourcePcsType is IccColorSpaceType.CieXyz && targetPcsType is IccColorSpaceType.CieLab)
{
// Convert from XYZ to Lab.
using IMemoryOwner<CieXyz> pcsFromBuffer = converter.Options.MemoryAllocator.Allocate<CieXyz>(source.Length);
Span<CieXyz> pcsFrom = pcsFromBuffer.GetSpan();
CieXyz.FromScaledVector4(pcsNormalized, pcsFrom);
using IMemoryOwner<CieLab> pcsToBuffer = converter.Options.MemoryAllocator.Allocate<CieLab>(source.Length);
Span<CieLab> pcsTo = pcsToBuffer.GetSpan();
pcsConverter.Convert<CieXyz, CieLab>(pcsFrom, pcsTo);
// Convert to the target normalized PCS space.
CieLab.ToScaledVector4(pcsTo, pcsNormalized);
}
else if (sourcePcsType is IccColorSpaceType.CieXyz && targetPcsType is IccColorSpaceType.CieXyz)
{
// Convert from XYZ to XYZ.
using IMemoryOwner<CieXyz> pcsFromToBuffer = converter.Options.MemoryAllocator.Allocate<CieXyz>(source.Length);
Span<CieXyz> pcsFromTo = pcsFromToBuffer.GetSpan();
CieXyz.FromScaledVector4(pcsNormalized, pcsFromTo);
pcsConverter.Convert<CieXyz, CieXyz>(pcsFromTo, pcsFromTo);
// Convert to the target normalized PCS space.
CieXyz.ToScaledVector4(pcsFromTo, pcsNormalized);
}
else if (sourcePcsType is IccColorSpaceType.CieLab && targetPcsType is IccColorSpaceType.CieLab)
{
// Convert from Lab to Lab.
if (sourceVersion.Major == 4 && targetVersion.Major == 2)
{
// Convert from Lab v4 to Lab v2.
LabToLabV2(pcsNormalized, pcsNormalized);
}
else if (sourceVersion.Major == 2 && targetVersion.Major == 4)
{
// Convert from Lab v2 to Lab v4.
LabV2ToLab(pcsNormalized, pcsNormalized);
}
using IMemoryOwner<CieLab> pcsFromToBuffer = converter.Options.MemoryAllocator.Allocate<CieLab>(source.Length);
Span<CieLab> pcsFromTo = pcsFromToBuffer.GetSpan();
CieLab.FromScaledVector4(pcsNormalized, pcsFromTo);
pcsConverter.Convert<CieLab, CieLab>(pcsFromTo, pcsFromTo);
// Convert to the target normalized PCS space.
CieLab.ToScaledVector4(pcsFromTo, pcsNormalized);
}
// Convert to the target space.
targetConverter.Calculate(pcsNormalized, pcsNormalized);
TTo.FromScaledVector4(pcsNormalized, destination);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 LabToLabV2(Vector4 input)
=> input * 65280F / 65535F;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 LabV2ToLab(Vector4 input)
=> input * 65535F / 65280F;
private static void LabToLabV2(Span<Vector4> source, Span<Vector4> destination)
=> LabToLab(source, destination, 65280F / 65535F);
private static void LabV2ToLab(Span<Vector4> source, Span<Vector4> destination)
=> LabToLab(source, destination, 65535F / 65280F);
private static void LabToLab(Span<Vector4> source, Span<Vector4> destination, [ConstantExpected] float scale)
{
if (Vector.IsHardwareAccelerated)
{
Vector<float> vScale = new(scale);
int i = 0;
// SIMD loop
int simdBatchSize = Vector<float>.Count / 4; // Number of Vector4 elements per SIMD batch
for (; i <= source.Length - simdBatchSize; i += simdBatchSize)
{
// Load the vector from source span
Vector<float> v = Unsafe.ReadUnaligned<Vector<float>>(ref Unsafe.As<Vector4, byte>(ref source[i]));
// Scale the vector
v *= vScale;
// Write the scaled vector to the destination span
Unsafe.WriteUnaligned(ref Unsafe.As<Vector4, byte>(ref destination[i]), v);
}
// Scalar fallback for remaining elements
for (; i < source.Length; i++)
{
destination[i] = source[i] * scale;
}
}
else
{
// Scalar fallback if SIMD is not supported
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i] * scale;
}
}
}
}

11
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs

@ -12,6 +12,11 @@ internal static class ColorProfileConverterExtensionsRgbCieLab
where TFrom : struct, IColorProfile<TFrom, Rgb>
where TTo : struct, IColorProfile<TTo, CieLab>
{
if (converter.ShouldUseIccProfiles())
{
return converter.ConvertUsingIccProfile<TFrom, TTo>(source);
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS
@ -33,6 +38,12 @@ internal static class ColorProfileConverterExtensionsRgbCieLab
where TFrom : struct, IColorProfile<TFrom, Rgb>
where TTo : struct, IColorProfile<TTo, CieLab>
{
if (converter.ShouldUseIccProfiles())
{
converter.ConvertUsingIccProfile(source, destination);
return;
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS.

11
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs

@ -12,6 +12,11 @@ internal static class ColorProfileConverterExtensionsRgbCieXyz
where TFrom : struct, IColorProfile<TFrom, Rgb>
where TTo : struct, IColorProfile<TTo, CieXyz>
{
if (converter.ShouldUseIccProfiles())
{
return converter.ConvertUsingIccProfile<TFrom, TTo>(source);
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS
@ -32,6 +37,12 @@ internal static class ColorProfileConverterExtensionsRgbCieXyz
where TFrom : struct, IColorProfile<TFrom, Rgb>
where TTo : struct, IColorProfile<TTo, CieXyz>
{
if (converter.ShouldUseIccProfiles())
{
converter.ConvertUsingIccProfile(source, destination);
return;
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS.

11
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs

@ -12,6 +12,11 @@ internal static class ColorProfileConverterExtensionsRgbRgb
where TFrom : struct, IColorProfile<TFrom, Rgb>
where TTo : struct, IColorProfile<TTo, Rgb>
{
if (converter.ShouldUseIccProfiles())
{
return converter.ConvertUsingIccProfile<TFrom, TTo>(source);
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS
@ -33,6 +38,12 @@ internal static class ColorProfileConverterExtensionsRgbRgb
where TFrom : struct, IColorProfile<TFrom, Rgb>
where TTo : struct, IColorProfile<TTo, Rgb>
{
if (converter.ShouldUseIccProfiles())
{
converter.ConvertUsingIccProfile(source, destination);
return;
}
ColorConversionOptions options = converter.Options;
// Convert to input PCS.

33
src/ImageSharp/ColorProfiles/Hsl.cs

@ -4,6 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.ColorProfiles;
@ -83,6 +84,38 @@ public readonly struct Hsl : IColorProfile<Hsl, Rgb>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Hsl left, Hsl right) => !left.Equals(right);
/// <inheritdoc/>
public Vector4 ToScaledVector4()
=> new(this.AsVector3Unsafe() / 360F, 1F);
/// <inheritdoc/>
public static Hsl FromScaledVector4(Vector4 source)
=> new(source.AsVector128().AsVector3() * 360F);
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<Hsl> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i].ToScaledVector4();
}
}
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = FromScaledVector4(source[i]);
}
}
/// <inheritdoc/>
public static Hsl FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source)
{

33
src/ImageSharp/ColorProfiles/Hsv.cs

@ -4,6 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.ColorProfiles;
@ -81,6 +82,38 @@ public readonly struct Hsv : IColorProfile<Hsv, Rgb>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Hsv left, Hsv right) => !left.Equals(right);
/// <inheritdoc/>
public Vector4 ToScaledVector4()
=> new(this.AsVector3Unsafe() / 360F, 1F);
/// <inheritdoc/>
public static Hsv FromScaledVector4(Vector4 source)
=> new(source.AsVector128().AsVector3() * 360F);
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<Hsv> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i].ToScaledVector4();
}
}
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = FromScaledVector4(source[i]);
}
}
/// <inheritdoc/>
public static Hsv FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source)
{

46
src/ImageSharp/ColorProfiles/HunterLab.cs

@ -4,6 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.ColorProfiles;
@ -80,6 +81,49 @@ public readonly struct HunterLab : IColorProfile<HunterLab, CieXyz>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(HunterLab left, HunterLab right) => !left.Equals(right);
/// <inheritdoc/>
public Vector4 ToScaledVector4()
{
Vector3 v3 = default;
v3 += this.AsVector3Unsafe();
v3 += new Vector3(0, 128F, 128F);
v3 /= new Vector3(100F, 255F, 255F);
return new Vector4(v3, 1F);
}
/// <inheritdoc/>
public static HunterLab FromScaledVector4(Vector4 source)
{
Vector3 v3 = source.AsVector128().AsVector3();
v3 *= new Vector3(100F, 255, 255);
v3 -= new Vector3(0, 128F, 128F);
return new HunterLab(v3);
}
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<HunterLab> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i].ToScaledVector4();
}
}
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<HunterLab> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = FromScaledVector4(source[i]);
}
}
/// <inheritdoc/>
public static HunterLab FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
{
@ -127,7 +171,7 @@ public readonly struct HunterLab : IColorProfile<HunterLab, CieXyz>
{
// Conversion algorithm described here:
// http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
CieXyz whitePoint = options.WhitePoint;
CieXyz whitePoint = options.SourceWhitePoint;
float l = this.L, a = this.A, b = this.B;
float xn = whitePoint.X, yn = whitePoint.Y, zn = whitePoint.Z;

46
src/ImageSharp/ColorProfiles/IColorProfile.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
namespace SixLabors.ImageSharp.ColorProfiles;
/// <summary>
@ -15,18 +17,58 @@ public interface IColorProfile
public static abstract ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource();
}
/// <summary>
/// Defines the contract for all color profiles.
/// </summary>
/// <typeparam name="TSelf">The type of color profile.</typeparam>
public interface IColorProfile<TSelf> : IColorProfile, IEquatable<TSelf>
where TSelf : IColorProfile<TSelf>
{
/// <summary>
/// Expands the pixel into a generic ("scaled") <see cref="Vector4"/> representation
/// with values scaled and clamped between <value>0</value> and <value>1</value>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToScaledVector4();
/// <summary>
/// Initializes the color instance from a generic a generic ("scaled") <see cref="Vector4"/> representation
/// with values scaled and clamped between <value>0</value> and <value>1</value>.
/// </summary>
/// <param name="source">The vector to load the pixel from.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>
public static abstract TSelf FromScaledVector4(Vector4 source);
/// <summary>
/// Converts the span of colors to a generic ("scaled") <see cref="Vector4"/> representation
/// with values scaled and clamped between <value>0</value> and <value>1</value>.
/// </summary>
/// <param name="source">The color span to convert from.</param>
/// <param name="destination">The vector span to write the results to.</param>
public static abstract void ToScaledVector4(ReadOnlySpan<TSelf> source, Span<Vector4> destination);
/// <summary>
/// Converts the span of colors from a generic ("scaled") <see cref="Vector4"/> representation
/// with values scaled and clamped between <value>0</value> and <value>1</value>.
/// </summary>
/// <param name="source">The vector span to convert from.</param>
/// <param name="destination">The color span to write the results to.</param>
public static abstract void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<TSelf> destination);
}
/// <summary>
/// Defines the contract for all color profiles.
/// </summary>
/// <typeparam name="TSelf">The type of color profile.</typeparam>
/// <typeparam name="TProfileSpace">The type of color profile connecting space.</typeparam>
public interface IColorProfile<TSelf, TProfileSpace> : IColorProfile, IEquatable<TSelf>
public interface IColorProfile<TSelf, TProfileSpace> : IColorProfile<TSelf>
where TSelf : IColorProfile<TSelf, TProfileSpace>
where TProfileSpace : struct, IProfileConnectingSpace
{
#pragma warning disable CA1000 // Do not declare static members on generic types
/// <summary>
/// Converts the color from the profile connection space.
/// Initializes the color instance from the profile connection space.
/// </summary>
/// <param name="options">The color profile conversion options.</param>
/// <param name="source">The color profile connecting space.</param>

15
src/ImageSharp/ColorProfiles/Icc/IccConverterbase.cs

@ -31,4 +31,19 @@ internal abstract partial class IccConverterBase
/// <returns>The converted value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 Calculate(Vector4 value) => this.calculator.Calculate(value);
/// <summary>
/// Converts colors with the initially provided ICC profile
/// </summary>
/// <param name="source">The source colors</param>
/// <param name="destination">The destination colors</param>
public void Calculate(ReadOnlySpan<Vector4> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
for (int i = 0; i < source.Length; i++)
{
destination[i] = this.Calculate(source[i]);
}
}
}

2
src/ImageSharp/ColorProfiles/Icc/IccProfileConverter.cs

@ -42,7 +42,7 @@ internal static class IccProfileConverter
{
ColorProfileConverter converter = new(new ColorConversionOptions()
{
WhitePoint = new CieXyz(inputIccProfile.Header.PcsIlluminant),
SourceWhitePoint = new CieXyz(inputIccProfile.Header.PcsIlluminant),
TargetWhitePoint = new CieXyz(outputIccProfile.Header.PcsIlluminant),
});

44
src/ImageSharp/ColorProfiles/Lms.cs

@ -4,6 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.ColorProfiles;
@ -89,6 +90,49 @@ public readonly struct Lms : IColorProfile<Lms, CieXyz>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 ToVector3() => new(this.L, this.M, this.S);
/// <inheritdoc/>
public Vector4 ToScaledVector4()
{
Vector3 v3 = default;
v3 += this.AsVector3Unsafe();
v3 += new Vector3(1F);
v3 /= 2F;
return new Vector4(v3, 1F);
}
/// <inheritdoc/>
public static Lms FromScaledVector4(Vector4 source)
{
Vector3 v3 = source.AsVector128().AsVector3();
v3 *= 2F;
v3 -= new Vector3(1F);
return new Lms(v3);
}
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<Lms> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i].ToScaledVector4();
}
}
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<Lms> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = FromScaledVector4(source[i]);
}
}
/// <inheritdoc/>
public static Lms FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
{

97
src/ImageSharp/ColorProfiles/Rgb.cs

@ -4,6 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces;
namespace SixLabors.ImageSharp.ColorProfiles;
@ -81,6 +82,49 @@ public readonly struct Rgb : IProfileConnectingSpace<Rgb, CieXyz>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Rgb left, Rgb right) => !left.Equals(right);
/// <summary>
/// Initializes the color instance from a generic scaled <see cref="Vector4"/>.
/// </summary>
/// <param name="source">The vector to load the color from.</param>
/// <returns>The <see cref="Rgb"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rgb FromScaledVector4(Vector4 source)
=> new(source.AsVector128().AsVector3());
/// <summary>
/// Expands the color into a generic ("scaled") <see cref="Vector4"/> representation
/// with values scaled and usually clamped between <value>0</value> and <value>1</value>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
=> new(this.ToScaledVector3(), 1F);
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<Rgb> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i].ToScaledVector4();
}
}
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = FromScaledVector4(source[i]);
}
}
/// <inheritdoc/>
public static Rgb FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
{
@ -108,10 +152,10 @@ public readonly struct Rgb : IProfileConnectingSpace<Rgb, CieXyz>
public CieXyz ToProfileConnectingSpace(ColorConversionOptions options)
{
// First expand to linear rgb
Rgb linear = FromScaledVector4(options.RgbWorkingSpace.Expand(this.ToScaledVector4()));
Rgb linear = FromScaledVector4(options.SourceRgbWorkingSpace.Expand(this.ToScaledVector4()));
// Then convert to xyz
return new CieXyz(Vector3.Transform(linear.ToScaledVector3(), GetRgbToCieXyzMatrix(options.RgbWorkingSpace)));
return new CieXyz(Vector3.Transform(linear.ToScaledVector3(), GetRgbToCieXyzMatrix(options.SourceRgbWorkingSpace)));
}
/// <inheritdoc/>
@ -119,13 +163,13 @@ public readonly struct Rgb : IProfileConnectingSpace<Rgb, CieXyz>
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
Matrix4x4 matrix = GetRgbToCieXyzMatrix(options.RgbWorkingSpace);
Matrix4x4 matrix = GetRgbToCieXyzMatrix(options.SourceRgbWorkingSpace);
for (int i = 0; i < source.Length; i++)
{
Rgb rgb = source[i];
// First expand to linear rgb
Rgb linear = FromScaledVector4(options.RgbWorkingSpace.Expand(rgb.ToScaledVector4()));
Rgb linear = FromScaledVector4(options.SourceRgbWorkingSpace.Expand(rgb.ToScaledVector4()));
// Then convert to xyz
destination[i] = new CieXyz(Vector3.Transform(linear.ToScaledVector3(), matrix));
@ -133,7 +177,8 @@ public readonly struct Rgb : IProfileConnectingSpace<Rgb, CieXyz>
}
/// <inheritdoc/>
public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.RgbWorkingSpace;
public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource()
=> ChromaticAdaptionWhitePointSource.RgbWorkingSpace;
/// <summary>
/// Initializes the color instance from a generic scaled <see cref="Vector3"/>.
@ -141,19 +186,8 @@ public readonly struct Rgb : IProfileConnectingSpace<Rgb, CieXyz>
/// <param name="source">The vector to load the color from.</param>
/// <returns>The <see cref="Rgb"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rgb FromScaledVector3(Vector3 source) => new(Vector3.Clamp(source, Vector3.Zero, Vector3.One));
/// <summary>
/// Initializes the color instance from a generic scaled <see cref="Vector4"/>.
/// </summary>
/// <param name="source">The vector to load the color from.</param>
/// <returns>The <see cref="Rgb"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rgb FromScaledVector4(Vector4 source)
{
source = Vector4.Clamp(source, Vector4.Zero, Vector4.One);
return new(source.X, source.Y, source.Z);
}
public static Rgb FromScaledVector3(Vector3 source)
=> new(source);
/// <summary>
/// Initializes the color instance for a source clamped between <value>0</value> and <value>1</value>
@ -161,7 +195,8 @@ public readonly struct Rgb : IProfileConnectingSpace<Rgb, CieXyz>
/// <param name="source">The source to load the color from.</param>
/// <returns>The <see cref="Rgb"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rgb Clamp(Rgb source) => new(Vector3.Clamp(new(source.R, source.G, source.B), Vector3.Zero, Vector3.One));
public static Rgb Clamp(Rgb source)
=> new(Vector3.Clamp(source.AsVector3Unsafe(), Vector3.Zero, Vector3.One));
/// <summary>
/// Expands the color into a generic ("scaled") <see cref="Vector3"/> representation
@ -170,24 +205,12 @@ public readonly struct Rgb : IProfileConnectingSpace<Rgb, CieXyz>
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 ToScaledVector3() => Clamp(this).ToVector3();
/// <summary>
/// Expands the color into a generic <see cref="Vector3"/> representation.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 ToVector3() => new(this.R, this.G, this.B);
/// <summary>
/// Expands the color into a generic ("scaled") <see cref="Vector4"/> representation
/// with values scaled and usually clamped between <value>0</value> and <value>1</value>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4() => new(this.ToScaledVector3(), 1f);
public Vector3 ToScaledVector3()
{
Vector3 v3 = default;
v3 += this.AsVector3Unsafe();
return v3;
}
/// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);

42
src/ImageSharp/ColorProfiles/YCbCr.cs

@ -4,6 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.ColorProfiles;
@ -82,6 +83,47 @@ public readonly struct YCbCr : IColorProfile<YCbCr, Rgb>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(YCbCr left, YCbCr right) => !left.Equals(right);
/// <inheritdoc/>
public Vector4 ToScaledVector4()
{
Vector3 v3 = default;
v3 += this.AsVector3Unsafe();
v3 /= Max;
return new Vector4(v3, 1F);
}
/// <inheritdoc/>
public static YCbCr FromScaledVector4(Vector4 source)
{
Vector3 v3 = source.AsVector128().AsVector3();
v3 *= Max;
return new YCbCr(v3);
}
/// <inheritdoc/>
public static void ToScaledVector4(ReadOnlySpan<YCbCr> source, Span<Vector4> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = source[i].ToScaledVector4();
}
}
/// <inheritdoc/>
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
// TODO: Optimize via SIMD
for (int i = 0; i < source.Length; i++)
{
destination[i] = FromScaledVector4(source[i]);
}
}
/// <inheritdoc/>
public static YCbCr FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source)
{

1
src/ImageSharp/Formats/DecoderOptions.cs

@ -55,6 +55,7 @@ public sealed class DecoderOptions
/// </summary>
public uint MaxFrames { get => this.maxFrames; init => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); }
/// <summary>
/// Gets the segment error handling strategy to use during decoding.
/// </summary>
public SegmentIntegrityHandling SegmentIntegrityHandling { get; init; } = SegmentIntegrityHandling.IgnoreNonCritical;

3
src/ImageSharp/PixelFormats/IPixel.cs

@ -23,7 +23,8 @@ public interface IPixel<TSelf> : IPixel, IEquatable<TSelf>
static abstract PixelOperations<TSelf> CreatePixelOperations();
/// <summary>
/// Initializes the pixel instance from a generic scaled <see cref="Vector4"/>.
/// Initializes the pixel instance from a generic a generic ("scaled") <see cref="Vector4"/> representation
/// with values scaled and clamped between <value>0</value> and <value>1</value>
/// </summary>
/// <param name="source">The vector to load the pixel from.</param>
/// <returns>The <typeparamref name="TSelf"/>.</returns>

3
src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs

@ -61,7 +61,8 @@ public partial struct Rgb24 : IPixel<Rgb24>
/// <param name="color">The instance of <see cref="Rgb"/> to convert.</param>
/// <returns>An instance of <see cref="Rgb24"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Rgb24(Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1f));
public static implicit operator Rgb24(Rgb color)
=> FromScaledVector4(new Vector4(color.ToScaledVector3(), 1F));
/// <summary>
/// Compares two <see cref="Rgb24"/> objects for equality.

2
src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs

@ -187,7 +187,7 @@ public partial struct Rgba32 : IPixel<Rgba32>, IPackedVector<uint>
/// <param name="color">The instance of <see cref="Rgb"/> to convert.</param>
/// <returns>An instance of <see cref="Rgba32"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Rgba32(Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1F));
public static implicit operator Rgba32(Rgb color) => FromScaledVector4(new Vector4(color.ToScaledVector3(), 1F));
/// <summary>
/// Compares two <see cref="Rgba32"/> objects for equality.

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

@ -13,7 +13,7 @@ public class RgbWorkingSpaceAdapt
private static readonly RGBColor RGBColor = new(0.206162, 0.260277, 0.746717);
private static readonly ColorProfileConverter ColorProfileConverter = new(new ColorConversionOptions { RgbWorkingSpace = KnownRgbWorkingSpaces.WideGamutRgb, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb });
private static readonly ColorProfileConverter ColorProfileConverter = new(new ColorConversionOptions { SourceRgbWorkingSpace = KnownRgbWorkingSpaces.WideGamutRgb, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb });
private static readonly IColorConverter<RGBColor, RGBColor> ColourfulConverter = new ConverterBuilder().FromRGB(RGBWorkingSpaces.WideGamutRGB).ToRGB(RGBWorkingSpaces.sRGB).Build();

4
tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs

@ -31,7 +31,7 @@ public class CieLabAndCieLchConversionTests
// Arrange
CieLch input = new(l, c, h);
CieLab expected = new(l2, a, b);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D50 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D50 };
ColorProfileConverter converter = new(options);
Span<CieLch> inputSpan = new CieLch[5];
@ -66,7 +66,7 @@ public class CieLabAndCieLchConversionTests
// Arrange
CieLab input = new(l, a, b);
CieLch expected = new(l2, c, h);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D50 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D50 };
ColorProfileConverter converter = new(options);
Span<CieLab> inputSpan = new CieLab[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs

@ -24,7 +24,7 @@ public class CieLabAndCieLchuvConversionTests
// Arrange
CieLchuv input = new(l, c, h);
CieLab expected = new(l2, a, b);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 };
ColorProfileConverter converter = new(options);
Span<CieLchuv> inputSpan = new CieLchuv[5];
@ -53,7 +53,7 @@ public class CieLabAndCieLchuvConversionTests
// Arrange
CieLab input = new(l, a, b);
CieLchuv expected = new(l2, c, h);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLab> inputSpan = new CieLab[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs

@ -24,7 +24,7 @@ public class CieLabAndCieLuvConversionTests
// Arrange
CieLuv input = new(l, u, v);
CieLab expected = new(l2, a, b);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 };
ColorProfileConverter converter = new(options);
Span<CieLuv> inputSpan = new CieLuv[5];
@ -53,7 +53,7 @@ public class CieLabAndCieLuvConversionTests
// Arrange
CieLab input = new(l, a, b);
CieLuv expected = new(l2, u, v);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLab> inputSpan = new CieLab[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs

@ -20,7 +20,7 @@ public class CieLchAndCieLuvConversionTests
// Arrange
CieLch input = new(l, c, h);
CieLuv expected = new(l2, u, v);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLch> inputSpan = new CieLch[5];
@ -48,7 +48,7 @@ public class CieLchAndCieLuvConversionTests
// Arrange
CieLuv input = new(l2, u, v);
CieLch expected = new(l, c, h);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 };
ColorProfileConverter converter = new(options);
Span<CieLuv> inputSpan = new CieLuv[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs

@ -20,7 +20,7 @@ public class CieLchuvAndCieLchConversionTests
// Arrange
CieLch input = new(l2, c2, h2);
CieLchuv expected = new(l, c, h);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLch> inputSpan = new CieLch[5];
@ -48,7 +48,7 @@ public class CieLchuvAndCieLchConversionTests
// Arrange
CieLchuv input = new(l, c, h);
CieLch expected = new(l2, c2, h2);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 };
ColorProfileConverter converter = new(options);
Span<CieLchuv> inputSpan = new CieLchuv[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs

@ -31,7 +31,7 @@ public class CieLchuvAndCieLuvConversionTests
// Arrange
CieLchuv input = new(l, c, h);
CieLuv expected = new(l2, u, v);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLchuv> inputSpan = new CieLchuv[5];
@ -67,7 +67,7 @@ public class CieLchuvAndCieLuvConversionTests
// Arrange
CieLuv input = new(l, u, v);
CieLchuv expected = new(l2, c, h);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLuv> inputSpan = new CieLuv[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs

@ -20,7 +20,7 @@ public class CieLchuvAndCmykConversionTests
// Arrange
Cmyk input = new(c2, m, y, k);
CieLchuv expected = new(l, c, h);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<Cmyk> inputSpan = new Cmyk[5];
@ -49,7 +49,7 @@ public class CieLchuvAndCmykConversionTests
// Arrange
CieLchuv input = new(l, c, h);
Cmyk expected = new(c2, m, y, k);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLchuv> inputSpan = new CieLchuv[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs

@ -20,7 +20,7 @@ public class CieLuvAndCieXyyConversionTests
// Arrange
CieLuv input = new(l, u, v);
CieXyy expected = new(x, y, yl);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLuv> inputSpan = new CieLuv[5];
@ -49,7 +49,7 @@ public class CieLuvAndCieXyyConversionTests
// Arrange
CieXyy input = new(x, y, yl);
CieLuv expected = new(l, u, v);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieXyy> inputSpan = new CieXyy[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs

@ -20,7 +20,7 @@ public class CieLuvAndHslConversionTests
// Arrange
CieLuv input = new(l, u, v);
Hsl expected = new(h, s, l2);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLuv> inputSpan = new CieLuv[5];
@ -49,7 +49,7 @@ public class CieLuvAndHslConversionTests
// Arrange
Hsl input = new(h, s, l2);
CieLuv expected = new(l, u, v);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<Hsl> inputSpan = new Hsl[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs

@ -20,7 +20,7 @@ public class CieLuvAndHsvConversionTests
// Arrange
CieLuv input = new(l, u, v);
Hsv expected = new(h, s, v2);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLuv> inputSpan = new CieLuv[5];
@ -49,7 +49,7 @@ public class CieLuvAndHsvConversionTests
// Arrange
Hsv input = new(h, s, v2);
CieLuv expected = new(l, u, v);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<Hsv> inputSpan = new Hsv[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs

@ -20,7 +20,7 @@ public class CieLuvAndHunterLabConversionTests
// Arrange
CieLuv input = new(l, u, v);
HunterLab expected = new(l2, a, b);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 };
ColorProfileConverter converter = new(options);
Span<CieLuv> inputSpan = new CieLuv[5];
@ -49,7 +49,7 @@ public class CieLuvAndHunterLabConversionTests
// Arrange
HunterLab input = new(l2, a, b);
CieLuv expected = new(l, u, v);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<HunterLab> inputSpan = new HunterLab[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs

@ -20,7 +20,7 @@ public class CieLuvAndLmsConversionTests
// Arrange
CieLuv input = new(l, u, v);
Lms expected = new(l2, m, s);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLuv> inputSpan = new CieLuv[5];
@ -49,7 +49,7 @@ public class CieLuvAndLmsConversionTests
// Arrange
Lms input = new(l2, m, s);
CieLuv expected = new(l, u, v);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<Lms> inputSpan = new Lms[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs

@ -20,7 +20,7 @@ public class CieLuvAndRgbConversionTests
// Arrange
CieLuv input = new(l, u, v);
Rgb expected = new(r, g, b);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLuv> inputSpan = new CieLuv[5];
@ -49,7 +49,7 @@ public class CieLuvAndRgbConversionTests
// Arrange
Rgb input = new(r, g, b);
CieLuv expected = new(l, u, v);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<Rgb> inputSpan = new Rgb[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs

@ -20,7 +20,7 @@ public class CieLuvAndYCbCrConversionTests
// Arrange
CieLuv input = new(l, u, v);
YCbCr expected = new(y, cb, cr);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLuv> inputSpan = new CieLuv[5];
@ -49,7 +49,7 @@ public class CieLuvAndYCbCrConversionTests
// Arrange
YCbCr input = new(y, cb, cr);
CieLuv expected = new(l, u, v);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<YCbCr> inputSpan = new YCbCr[5];

4
tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs

@ -30,7 +30,7 @@ public class CieXyzAndCieLabConversionTest
{
// Arrange
CieLab input = new(l, a, b);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
CieXyz expected = new(x, y, z);
@ -63,7 +63,7 @@ public class CieXyzAndCieLabConversionTest
{
// Arrange
CieXyz input = new(x, y, z);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
CieLab expected = new(l, a, b);

4
tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs

@ -29,7 +29,7 @@ public class CieXyzAndCieLuvConversionTest
CieXyz input = new(x, y, z);
CieLuv expected = new(l, u, v);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieXyz> inputSpan = new CieXyz[5];
@ -64,7 +64,7 @@ public class CieXyzAndCieLuvConversionTest
CieLuv input = new(l, u, v);
CieXyz expected = new(x, y, z);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 };
ColorProfileConverter converter = new(options);
Span<CieLuv> inputSpan = new CieLuv[5];

16
tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs

@ -26,7 +26,7 @@ public class ColorProfileConverterChomaticAdaptationTests
Rgb expected = new(r2, g2, b2);
ColorConversionOptions options = new()
{
RgbWorkingSpace = KnownRgbWorkingSpaces.WideGamutRgb,
SourceRgbWorkingSpace = KnownRgbWorkingSpaces.WideGamutRgb,
TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb
};
ColorProfileConverter converter = new(options);
@ -49,7 +49,7 @@ public class ColorProfileConverterChomaticAdaptationTests
Rgb expected = new(r2, g2, b2);
ColorConversionOptions options = new()
{
RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb,
SourceRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb,
TargetRgbWorkingSpace = KnownRgbWorkingSpaces.WideGamutRgb
};
ColorProfileConverter converter = new(options);
@ -71,7 +71,7 @@ public class ColorProfileConverterChomaticAdaptationTests
CieLab expected = new(l2, a2, b2);
ColorConversionOptions options = new()
{
WhitePoint = KnownIlluminants.D65,
SourceWhitePoint = KnownIlluminants.D65,
TargetWhitePoint = KnownIlluminants.D50
};
ColorProfileConverter converter = new(options);
@ -93,7 +93,7 @@ public class ColorProfileConverterChomaticAdaptationTests
CieXyz expected = new(x2, y2, z2);
ColorConversionOptions options = new()
{
WhitePoint = KnownIlluminants.D65,
SourceWhitePoint = KnownIlluminants.D65,
TargetWhitePoint = KnownIlluminants.D50,
AdaptationMatrix = KnownChromaticAdaptationMatrices.Bradford
};
@ -117,7 +117,7 @@ public class ColorProfileConverterChomaticAdaptationTests
CieXyz expected = new(x2, y2, z2);
ColorConversionOptions options = new()
{
WhitePoint = KnownIlluminants.D65,
SourceWhitePoint = KnownIlluminants.D65,
TargetWhitePoint = KnownIlluminants.D50,
AdaptationMatrix = KnownChromaticAdaptationMatrices.XyzScaling
};
@ -141,7 +141,7 @@ public class ColorProfileConverterChomaticAdaptationTests
HunterLab expected = new(l2, a2, b2);
ColorConversionOptions options = new()
{
WhitePoint = KnownIlluminants.D65,
SourceWhitePoint = KnownIlluminants.D65,
TargetWhitePoint = KnownIlluminants.D50,
};
@ -164,7 +164,7 @@ public class ColorProfileConverterChomaticAdaptationTests
CieLchuv expected = new(l2, c2, h2);
ColorConversionOptions options = new()
{
WhitePoint = KnownIlluminants.D65,
SourceWhitePoint = KnownIlluminants.D65,
TargetWhitePoint = KnownIlluminants.D50,
AdaptationMatrix = KnownChromaticAdaptationMatrices.XyzScaling
};
@ -187,7 +187,7 @@ public class ColorProfileConverterChomaticAdaptationTests
CieLch expected = new(l2, c2, h2);
ColorConversionOptions options = new()
{
WhitePoint = KnownIlluminants.D65,
SourceWhitePoint = KnownIlluminants.D65,
TargetWhitePoint = KnownIlluminants.D50,
AdaptationMatrix = KnownChromaticAdaptationMatrices.XyzScaling
};

4
tests/ImageSharp.Tests/ColorProfiles/Icc/Calculators/LutEntryCalculatorTests.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc.Calculators
[Trait("Color", "Conversion")]
public class LutEntryCalculatorTests
{
[Theory]
[Theory(Skip = "Results do not match not expected.")]
[MemberData(nameof(IccConversionDataLutEntry.Lut8ConversionTestData), MemberType = typeof(IccConversionDataLutEntry))]
internal void LutEntryCalculator_WithLut8_ReturnsResult(IccLut8TagDataEntry lut, Vector4 input, Vector4 expected)
{
@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc.Calculators
VectorAssert.Equal(expected, result, 4);
}
[Theory]
[Theory(Skip = "Results do not match not expected.")]
[MemberData(nameof(IccConversionDataLutEntry.Lut16ConversionTestData), MemberType = typeof(IccConversionDataLutEntry))]
internal void LutEntryCalculator_WithLut16_ReturnsResult(IccLut16TagDataEntry lut, Vector4 input, Vector4 expected)
{

8
tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs

@ -27,7 +27,7 @@ public class RgbAndCieXyzConversionTest
{
// Arrange
CieXyz input = new(x, y, z);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D50, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb };
ColorProfileConverter converter = new(options);
Rgb expected = new(r, g, b);
@ -60,7 +60,7 @@ public class RgbAndCieXyzConversionTest
{
// Arrange
CieXyz input = new(x, y, z);
ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb };
ColorConversionOptions options = new() { SourceWhitePoint = KnownIlluminants.D65, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb };
ColorProfileConverter converter = new(options);
Rgb expected = new(r, g, b);
@ -93,7 +93,7 @@ public class RgbAndCieXyzConversionTest
{
// Arrange
Rgb input = new(r, g, b);
ColorConversionOptions options = new() { TargetWhitePoint = KnownIlluminants.D50, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb };
ColorConversionOptions options = new() { TargetWhitePoint = KnownIlluminants.D50, SourceRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb };
ColorProfileConverter converter = new(options);
CieXyz expected = new(x, y, z);
@ -126,7 +126,7 @@ public class RgbAndCieXyzConversionTest
{
// Arrange
Rgb input = new(r, g, b);
ColorConversionOptions options = new() { TargetWhitePoint = KnownIlluminants.D65, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb };
ColorConversionOptions options = new() { TargetWhitePoint = KnownIlluminants.D65, SourceRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb };
ColorProfileConverter converter = new(options);
CieXyz expected = new(x, y, z);

2
tests/ImageSharp.Tests/ColorProfiles/VonKriesChromaticAdaptationTests.cs

@ -20,7 +20,7 @@ public class VonKriesChromaticAdaptationTests
{
ColorConversionOptions options = new()
{
WhitePoint = from,
SourceWhitePoint = from,
TargetWhitePoint = to
};

Loading…
Cancel
Save