Browse Source

Merge ab7effe1c5 into 59ce6af6fc

pull/3181/merge
Stefan Nikolei 3 weeks ago
committed by GitHub
parent
commit
dadbce5a38
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs
  2. 4
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs
  3. 4
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs
  4. 4
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs
  5. 4
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs
  6. 4
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs
  7. 4
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs
  8. 4
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs
  9. 4
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs
  10. 70
      src/ImageSharp/ColorProfiles/Rgb.cs
  11. 58
      src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs
  12. 69
      src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs
  13. 9
      src/ImageSharp/Metadata/Profiles/ICC/IccProfile.SRGB.cs

4
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs

@ -41,7 +41,7 @@ public static class ColorProfileConverterExtensionsCieLabCieLab
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix);
pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert between PCS
CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB);
@ -87,7 +87,7 @@ public static class ColorProfileConverterExtensionsCieLabCieLab
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix);
VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert between PCS.
CieLab.FromProfileConnectionSpace(options, pcsFrom, pcsFromTo);

4
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs

@ -43,7 +43,7 @@ public static class ColorProfileConverterExtensionsCieLabCieXyz
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
pcsTo = VonKriesChromaticAdaptation.Transform(in pcsTo, whitePoints, options.AdaptationMatrix);
pcsTo = VonKriesChromaticAdaptation.Transform(in pcsTo, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert to output from PCS
return TTo.FromProfileConnectingSpace(options, in pcsTo);
@ -87,7 +87,7 @@ public static class ColorProfileConverterExtensionsCieLabCieXyz
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
VonKriesChromaticAdaptation.Transform(pcsTo, pcsTo, whitePoints, options.AdaptationMatrix);
VonKriesChromaticAdaptation.Transform(pcsTo, pcsTo, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert to output from PCS
TTo.FromProfileConnectionSpace(options, pcsTo, destination);

4
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs

@ -41,7 +41,7 @@ public static class ColorProfileConverterExtensionsCieLabRgb
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix);
pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert between PCS
Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB);
@ -87,7 +87,7 @@ public static class ColorProfileConverterExtensionsCieLabRgb
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
VonKriesChromaticAdaptation.Transform(pcsFromB, pcsFromB, whitePoints, options.AdaptationMatrix);
VonKriesChromaticAdaptation.Transform(pcsFromB, pcsFromB, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert between PCS.
using IMemoryOwner<Rgb> pcsToOwner = options.MemoryAllocator.Allocate<Rgb>(source.Length);

4
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs

@ -40,7 +40,7 @@ public static class ColorProfileConverterExtensionsCieXyzCieLab
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix);
pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert between PCS
CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFrom);
@ -82,7 +82,7 @@ public static class ColorProfileConverterExtensionsCieXyzCieLab
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix);
VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert between PCS.
using IMemoryOwner<CieLab> pcsToOwner = options.MemoryAllocator.Allocate<CieLab>(source.Length);

4
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs

@ -40,7 +40,7 @@ public static class ColorProfileConverterExtensionsCieXyzCieXyz
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix);
pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert to output from PCS
return TTo.FromProfileConnectingSpace(options, in pcsFrom);
@ -79,7 +79,7 @@ public static class ColorProfileConverterExtensionsCieXyzCieXyz
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix);
VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert to output from PCS
TTo.FromProfileConnectionSpace(options, pcsFrom, destination);

4
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs

@ -40,7 +40,7 @@ public static class ColorProfileConverterExtensionsCieXyzRgb
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix);
pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert between PCS
Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFrom);
@ -82,7 +82,7 @@ public static class ColorProfileConverterExtensionsCieXyzRgb
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix);
VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert between PCS.
using IMemoryOwner<Rgb> pcsToOwner = options.MemoryAllocator.Allocate<Rgb>(source.Length);

4
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs

@ -41,7 +41,7 @@ public static class ColorProfileConverterExtensionsRgbCieLab
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix);
pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert between PCS
CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB);
@ -87,7 +87,7 @@ public static class ColorProfileConverterExtensionsRgbCieLab
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
VonKriesChromaticAdaptation.Transform(pcsFromB, pcsFromB, whitePoints, options.AdaptationMatrix);
VonKriesChromaticAdaptation.Transform(pcsFromB, pcsFromB, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert between PCS.
using IMemoryOwner<CieLab> pcsToOwner = options.MemoryAllocator.Allocate<CieLab>(source.Length);

4
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs

@ -43,7 +43,7 @@ public static class ColorProfileConverterExtensionsRgbCieXyz
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
pcsTo = VonKriesChromaticAdaptation.Transform(in pcsTo, whitePoints, options.AdaptationMatrix);
pcsTo = VonKriesChromaticAdaptation.Transform(in pcsTo, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert to output from PCS
return TTo.FromProfileConnectingSpace(options, in pcsTo);
@ -87,7 +87,7 @@ public static class ColorProfileConverterExtensionsRgbCieXyz
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
VonKriesChromaticAdaptation.Transform(pcsTo, pcsTo, whitePoints, options.AdaptationMatrix);
VonKriesChromaticAdaptation.Transform(pcsTo, pcsTo, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert to output from PCS
TTo.FromProfileConnectionSpace(options, pcsTo, destination);

4
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs

@ -41,7 +41,7 @@ public static class ColorProfileConverterExtensionsRgbRgb
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix);
pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert between PCS
Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB);
@ -87,7 +87,7 @@ public static class ColorProfileConverterExtensionsRgbRgb
// Adapt to target white point
(CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints<TFrom, TTo>();
VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix);
VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix, options.InverseAdaptationMatrix);
// Convert between PCS.
Rgb.FromProfileConnectionSpace(options, pcsFrom, pcsFromTo);

70
src/ImageSharp/ColorProfiles/Rgb.cs

@ -272,7 +272,7 @@ public readonly struct Rgb : IProfileConnectingSpace<Rgb, CieXyz>
public static Rgb FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
{
// Convert to linear rgb then compress.
Rgb linear = new(Vector3.Transform(source.AsVector3Unsafe(), GetCieXyzToRgbMatrix(options.TargetRgbWorkingSpace)));
Rgb linear = new(Vector3.Transform(source.AsVector3Unsafe(), options.TargetRgbWorkingSpace.CieXyzToRgbMatrix));
return FromScaledVector4(options.TargetRgbWorkingSpace.Compress(linear.ToScaledVector4()));
}
@ -281,7 +281,7 @@ public readonly struct Rgb : IProfileConnectingSpace<Rgb, CieXyz>
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
Matrix4x4 matrix = GetCieXyzToRgbMatrix(options.TargetRgbWorkingSpace);
Matrix4x4 matrix = options.TargetRgbWorkingSpace.CieXyzToRgbMatrix;
for (int i = 0; i < source.Length; i++)
{
// Convert to linear rgb then compress.
@ -298,7 +298,7 @@ public readonly struct Rgb : IProfileConnectingSpace<Rgb, CieXyz>
Rgb linear = FromScaledVector4(options.SourceRgbWorkingSpace.Expand(this.ToScaledVector4()));
// Then convert to xyz
return new CieXyz(Vector3.Transform(linear.AsVector3Unsafe(), GetRgbToCieXyzMatrix(options.SourceRgbWorkingSpace)));
return new CieXyz(Vector3.Transform(linear.AsVector3Unsafe(), options.SourceRgbWorkingSpace.RgbToCieXyzMatrix));
}
/// <inheritdoc/>
@ -306,7 +306,7 @@ public readonly struct Rgb : IProfileConnectingSpace<Rgb, CieXyz>
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
Matrix4x4 matrix = GetRgbToCieXyzMatrix(options.SourceRgbWorkingSpace);
Matrix4x4 matrix = options.SourceRgbWorkingSpace.RgbToCieXyzMatrix;
for (int i = 0; i < source.Length; i++)
{
Rgb rgb = source[i];
@ -371,68 +371,6 @@ public readonly struct Rgb : IProfileConnectingSpace<Rgb, CieXyz>
internal Vector3 AsVector3Unsafe() => Unsafe.As<Rgb, Vector3>(ref Unsafe.AsRef(in this));
private static Matrix4x4 GetCieXyzToRgbMatrix(RgbWorkingSpace workingSpace)
{
Matrix4x4 matrix = GetRgbToCieXyzMatrix(workingSpace);
Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix);
return inverseMatrix;
}
private static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpace workingSpace)
{
DebugGuard.NotNull(workingSpace, nameof(workingSpace));
RgbPrimariesChromaticityCoordinates chromaticity = workingSpace.ChromaticityCoordinates;
float xr = chromaticity.R.X;
float xg = chromaticity.G.X;
float xb = chromaticity.B.X;
float yr = chromaticity.R.Y;
float yg = chromaticity.G.Y;
float yb = chromaticity.B.Y;
float mXr = xr / yr;
float mZr = (1 - xr - yr) / yr;
float mXg = xg / yg;
float mZg = (1 - xg - yg) / yg;
float mXb = xb / yb;
float mZb = (1 - xb - yb) / yb;
Matrix4x4 xyzMatrix = new()
{
M11 = mXr,
M21 = mXg,
M31 = mXb,
M12 = 1F,
M22 = 1F,
M32 = 1F,
M13 = mZr,
M23 = mZg,
M33 = mZb,
M44 = 1F
};
Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix);
Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.AsVector3Unsafe(), inverseXyzMatrix);
// Use transposed Rows/Columns
return new Matrix4x4
{
M11 = vector.X * mXr,
M21 = vector.Y * mXg,
M31 = vector.Z * mXb,
M12 = vector.X,
M22 = vector.Y,
M32 = vector.Z,
M13 = vector.X * mZr,
M23 = vector.Y * mZg,
M33 = vector.Z * mZb,
M44 = 1F
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector512<float> ReadVector512(ref float src)
{

58
src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs

@ -25,6 +25,47 @@ public static class VonKriesChromaticAdaptation
/// <param name="matrix">The chromatic adaptation matrix.</param>
/// <returns>The <see cref="CieXyz"/></returns>
public static CieXyz Transform(in CieXyz source, (CieXyz From, CieXyz To) whitePoints, Matrix4x4 matrix)
{
Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix);
return Transform(in source, whitePoints, matrix, inverseMatrix);
}
/// <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="whitePoints">The conversion white points.</param>
/// <param name="matrix">The chromatic adaptation matrix.</param>
public static void Transform(
ReadOnlySpan<CieXyz> source,
Span<CieXyz> destination,
(CieXyz From, CieXyz To) whitePoints,
Matrix4x4 matrix)
{
Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix);
Transform(source, destination, whitePoints, matrix, inverseMatrix);
}
/// <summary>
/// Performs a linear transformation of a source color in to the destination color using a
/// precomputed inverse of <paramref name="matrix"/>.
/// </summary>
/// <remarks>
/// For callers that already hold the inverse, e.g. <see cref="ColorConversionOptions.InverseAdaptationMatrix"/>,
/// to avoid repeating <see cref="Matrix4x4.Invert"/> on every call.
/// </remarks>
/// <param name="source">The source color.</param>
/// <param name="whitePoints">The conversion white points.</param>
/// <param name="matrix">The chromatic adaptation matrix.</param>
/// <param name="inverseMatrix">The inverse of <paramref name="matrix"/>.</param>
/// <returns>The <see cref="CieXyz"/></returns>
internal static CieXyz Transform(
in CieXyz source,
(CieXyz From, CieXyz To) whitePoints,
Matrix4x4 matrix,
Matrix4x4 inverseMatrix)
{
CieXyz from = whitePoints.From;
CieXyz to = whitePoints.To;
@ -41,23 +82,28 @@ public static class VonKriesChromaticAdaptation
Vector3 vector = targetWhitePointLms / sourceWhitePointLms;
Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms);
Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix);
return new CieXyz(Vector3.Transform(targetColorLms, inverseMatrix));
}
/// <summary>
/// Performs a bulk linear transformation of a source color in to the destination color.
/// Performs a bulk linear transformation of a source color in to the destination color using a
/// precomputed inverse of <paramref name="matrix"/>.
/// </summary>
/// <remarks>Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates).</remarks>
/// <remarks>
/// For callers that already hold the inverse, e.g. <see cref="ColorConversionOptions.InverseAdaptationMatrix"/>,
/// to avoid repeating <see cref="Matrix4x4.Invert"/> on every call.
/// </remarks>
/// <param name="source">The span to the source colors.</param>
/// <param name="destination">The span to the destination colors.</param>
/// <param name="whitePoints">The conversion white points.</param>
/// <param name="matrix">The chromatic adaptation matrix.</param>
public static void Transform(
/// <param name="inverseMatrix">The inverse of <paramref name="matrix"/>.</param>
internal static void Transform(
ReadOnlySpan<CieXyz> source,
Span<CieXyz> destination,
(CieXyz From, CieXyz To) whitePoints,
Matrix4x4 matrix)
Matrix4x4 matrix,
Matrix4x4 inverseMatrix)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -71,8 +117,6 @@ public static class VonKriesChromaticAdaptation
return;
}
Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix);
ref CieXyz sourceBase = ref MemoryMarshal.GetReference(source);
ref CieXyz destinationBase = ref MemoryMarshal.GetReference(destination);

69
src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs

@ -19,6 +19,12 @@ public abstract class RgbWorkingSpace
{
this.WhitePoint = referenceWhite;
this.ChromaticityCoordinates = chromaticityCoordinates;
Matrix4x4 rgbToCieXyz = CreateRgbToCieXyzMatrix(referenceWhite, chromaticityCoordinates);
this.RgbToCieXyzMatrix = rgbToCieXyz;
_ = Matrix4x4.Invert(rgbToCieXyz, out Matrix4x4 cieXyzToRgb);
this.CieXyzToRgbMatrix = cieXyzToRgb;
}
/// <summary>
@ -31,6 +37,17 @@ public abstract class RgbWorkingSpace
/// </summary>
public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; }
/// <summary>
/// Gets the matrix transforming linear rgb coordinates in this working space to CIE XYZ.
/// </summary>
internal Matrix4x4 RgbToCieXyzMatrix { get; }
/// <summary>
/// Gets the inverse of <see cref="RgbToCieXyzMatrix"/>, transforming CIE XYZ to linear rgb
/// coordinates in this working space.
/// </summary>
internal Matrix4x4 CieXyzToRgbMatrix { get; }
/// <summary>
/// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
/// </summary>
@ -84,4 +101,56 @@ public abstract class RgbWorkingSpace
/// <inheritdoc/>
public override int GetHashCode()
=> HashCode.Combine(this.GetType(), this.WhitePoint, this.ChromaticityCoordinates);
private static Matrix4x4 CreateRgbToCieXyzMatrix(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
{
float xr = chromaticityCoordinates.R.X;
float xg = chromaticityCoordinates.G.X;
float xb = chromaticityCoordinates.B.X;
float yr = chromaticityCoordinates.R.Y;
float yg = chromaticityCoordinates.G.Y;
float yb = chromaticityCoordinates.B.Y;
float mXr = xr / yr;
float mZr = (1 - xr - yr) / yr;
float mXg = xg / yg;
float mZg = (1 - xg - yg) / yg;
float mXb = xb / yb;
float mZb = (1 - xb - yb) / yb;
Matrix4x4 xyzMatrix = new()
{
M11 = mXr,
M21 = mXg,
M31 = mXb,
M12 = 1F,
M22 = 1F,
M32 = 1F,
M13 = mZr,
M23 = mZg,
M33 = mZb,
M44 = 1F
};
Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix);
Vector3 vector = Vector3.Transform(referenceWhite.AsVector3Unsafe(), inverseXyzMatrix);
// Use transposed Rows/Columns
return new Matrix4x4
{
M11 = vector.X * mXr,
M21 = vector.Y * mXg,
M31 = vector.Z * mXb,
M12 = vector.X,
M22 = vector.Y,
M32 = vector.Z,
M13 = vector.X * mZr,
M23 = vector.Y * mZg,
M33 = vector.Z * mZb,
M44 = 1F
};
}
}

9
src/ImageSharp/Metadata/Profiles/ICC/IccProfile.SRGB.cs

@ -26,7 +26,7 @@ public sealed partial class IccProfile
/// 3) Require rTRC, gTRC, bTRC to exist and be identical by parameters or sampled shape.
/// 4) Accept if rXYZ/gXYZ/bXYZ already match the D50-adapted sRGB colorants within tolerance.
/// 5) If white point ≈ D65, adapt only the colorant columns to D50 using Bradford
/// via <see cref="VonKriesChromaticAdaptation.Transform(in CieXyz, ValueTuple{CieXyz, CieXyz}, Matrix4x4)"/> and then compare.
/// via <see cref="VonKriesChromaticAdaptation.Transform(in CieXyz, ValueTuple{CieXyz, CieXyz}, Matrix4x4, Matrix4x4)"/> and then compare.
/// This rejects channel-swapped and appearance profiles while allowing real sRGB.
/// </summary>
/// <remarks>
@ -139,10 +139,11 @@ public sealed partial class IccProfile
CieXyz fromWp = new(wtpt); // Declared white
CieXyz toWp = KnownIlluminants.D50; // PCS white
Matrix4x4 matrix = KnownChromaticAdaptationMatrices.Bradford;
Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix);
rXYZ = VonKriesChromaticAdaptation.Transform(new CieXyz(rXYZ), (fromWp, toWp), matrix).AsVector3Unsafe();
gXYZ = VonKriesChromaticAdaptation.Transform(new CieXyz(gXYZ), (fromWp, toWp), matrix).AsVector3Unsafe();
bXYZ = VonKriesChromaticAdaptation.Transform(new CieXyz(bXYZ), (fromWp, toWp), matrix).AsVector3Unsafe();
rXYZ = VonKriesChromaticAdaptation.Transform(new CieXyz(rXYZ), (fromWp, toWp), matrix, inverseMatrix).AsVector3Unsafe();
gXYZ = VonKriesChromaticAdaptation.Transform(new CieXyz(gXYZ), (fromWp, toWp), matrix, inverseMatrix).AsVector3Unsafe();
bXYZ = VonKriesChromaticAdaptation.Transform(new CieXyz(bXYZ), (fromWp, toWp), matrix, inverseMatrix).AsVector3Unsafe();
}
// Require identity mapping of primaries, no permutation

Loading…
Cancel
Save