diff --git a/src/ImageSharp/ColorProfiles/ChromaticAdaptionWhitePointSource.cs b/src/ImageSharp/ColorProfiles/ChromaticAdaptionWhitePointSource.cs
new file mode 100644
index 0000000000..7e4a9c413b
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/ChromaticAdaptionWhitePointSource.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.ColorProfiles;
+
+///
+/// Enumerate the possible sources of the white point used in chromatic adaptation.
+///
+public enum ChromaticAdaptionWhitePointSource
+{
+ ///
+ /// The white point of the source color space.
+ ///
+ WhitePoint,
+
+ ///
+ /// The white point of the source working space.
+ ///
+ RgbWorkingSpace
+}
diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs
index 248559f731..3578e27139 100644
--- a/src/ImageSharp/ColorProfiles/CieLab.cs
+++ b/src/ImageSharp/ColorProfiles/CieLab.cs
@@ -172,4 +172,7 @@ public readonly struct CieLab : IProfileConnectingSpace
destination[i] = lab.ToProfileConnectingSpace(options);
}
}
+
+ ///
+ public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint;
}
diff --git a/src/ImageSharp/ColorProfiles/CieLch.cs b/src/ImageSharp/ColorProfiles/CieLch.cs
index 4b48e1c8d9..23f250d0c8 100644
--- a/src/ImageSharp/ColorProfiles/CieLch.cs
+++ b/src/ImageSharp/ColorProfiles/CieLch.cs
@@ -163,4 +163,7 @@ public readonly struct CieLch : IColorProfile
destination[i] = lch.ToProfileConnectingSpace(options);
}
}
+
+ ///
+ public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint;
}
diff --git a/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs
new file mode 100644
index 0000000000..b55abdd05a
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs
@@ -0,0 +1,83 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Runtime.CompilerServices;
+
+// ReSharper disable CompareOfFloatsByEqualityOperator
+namespace SixLabors.ImageSharp.ColorProfiles;
+
+///
+/// Represents the coordinates of CIEXY chromaticity space.
+///
+public readonly struct CieXyChromaticityCoordinates : IEquatable
+{
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// Chromaticity coordinate x (usually from 0 to 1)
+ /// Chromaticity coordinate y (usually from 0 to 1)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public CieXyChromaticityCoordinates(float x, float y)
+ {
+ this.X = x;
+ this.Y = y;
+ }
+
+ ///
+ /// Gets the chromaticity X-coordinate.
+ ///
+ ///
+ /// Ranges usually from 0 to 1.
+ ///
+ public readonly float X { get; }
+
+ ///
+ /// Gets the chromaticity Y-coordinate
+ ///
+ ///
+ /// Ranges usually from 0 to 1.
+ ///
+ public readonly float Y { get; }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ /// The on the left side of the operand.
+ /// The on the right side of the operand.
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right)
+ => left.Equals(right);
+
+ ///
+ /// Compares two objects for inequality
+ ///
+ /// The on the left side of the operand.
+ /// The on the right side of the operand.
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right)
+ => !left.Equals(right);
+
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public override int GetHashCode()
+ => HashCode.Combine(this.X, this.Y);
+
+ ///
+ public override string ToString()
+ => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})");
+
+ ///
+ public override bool Equals(object? obj)
+ => obj is CieXyChromaticityCoordinates other && this.Equals(other);
+
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public bool Equals(CieXyChromaticityCoordinates other)
+ => this.X.Equals(other.X) && this.Y.Equals(other.Y);
+}
diff --git a/src/ImageSharp/ColorProfiles/CieXyz.cs b/src/ImageSharp/ColorProfiles/CieXyz.cs
index b22ab16f92..ec28a232da 100644
--- a/src/ImageSharp/ColorProfiles/CieXyz.cs
+++ b/src/ImageSharp/ColorProfiles/CieXyz.cs
@@ -121,4 +121,7 @@ public readonly struct CieXyz : IProfileConnectingSpace
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
source.CopyTo(destination[..source.Length]);
}
+
+ ///
+ public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint;
}
diff --git a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs
index de32aa54d3..384ab6a7f7 100644
--- a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs
+++ b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs
@@ -2,6 +2,8 @@
// Licensed under the Six Labors Split License.
using System.Numerics;
+using SixLabors.ColorProfiles;
+using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.ColorProfiles;
@@ -33,6 +35,16 @@ public class ColorConversionOptions
///
public CieXyz TargetWhitePoint { get; init; } = Illuminants.D50;
+ ///
+ /// Gets the source working space used for companding in conversions from/to XYZ color space.
+ ///
+ public RgbWorkingSpace RgbWorkingSpace { get; init; } = RgbWorkingSpaces.SRgb;
+
+ ///
+ /// Gets the destination working space used for companding in conversions from/to XYZ color space.
+ ///
+ public RgbWorkingSpace TargetRgbWorkingSpace { get; init; } = RgbWorkingSpaces.SRgb;
+
///
/// Gets the transformation matrix used in conversion to perform chromatic adaptation.
///
diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs
index 481280b85e..8860777cd4 100644
--- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs
+++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs
@@ -21,7 +21,7 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz
CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options);
// Adapt to target white point
- VonKriesChromaticAdaptation.Transform(options, in pcsTo);
+ VonKriesChromaticAdaptation.Transform(options, in pcsTo);
// Convert to output from PCS
return TTo.FromProfileConnectingSpace(options, pcsTo);
@@ -44,7 +44,7 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz
CieLab.ToProfileConnectionSpace(options, pcsFrom, pcsTo);
// Adapt to target white point
- VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo);
+ VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo);
// Convert to output from PCS
TTo.FromProfileConnectionSpace(options, pcsTo, destination);
diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs
index 949cd6c8e9..50317311c0 100644
--- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs
+++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers;
@@ -18,7 +18,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab
CieXyz pcsFrom = source.ToProfileConnectingSpace(options);
// Adapt to target white point
- VonKriesChromaticAdaptation.Transform(options, in pcsFrom);
+ VonKriesChromaticAdaptation.Transform(options, in pcsFrom);
// Convert between PCS
CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFrom);
@@ -39,7 +39,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab
TFrom.ToProfileConnectionSpace(options, source, pcsFrom);
// Adapt to target white point
- VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom);
+ VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom);
// Convert between PCS.
using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length);
diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs
index ebe07003e9..2380bb15eb 100644
--- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs
+++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs
@@ -18,7 +18,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz
CieXyz pcsFrom = source.ToProfileConnectingSpace(options);
// Adapt to target white point
- VonKriesChromaticAdaptation.Transform(options, in pcsFrom);
+ VonKriesChromaticAdaptation.Transform(options, in pcsFrom);
// Convert between PCS
CieXyz pcsTo = CieXyz.FromProfileConnectingSpace(options, in pcsFrom);
@@ -39,7 +39,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz
TFrom.ToProfileConnectionSpace(options, source, pcsFrom);
// Adapt to target white point
- VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom);
+ VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom);
// Convert between PCS.
using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length);
diff --git a/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs b/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs
new file mode 100644
index 0000000000..44dae35a98
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs
@@ -0,0 +1,171 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Collections.Concurrent;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace SixLabors.ImageSharp.ColorProfiles.Companding;
+
+///
+/// Companding utilities that allow the accelerated compression-expansion of color channels.
+///
+public static class CompandingUtilities
+{
+ private const int Length = Scale + 2; // 256kb @ 16bit precision.
+ private const int Scale = (1 << 16) - 1;
+ private static readonly ConcurrentDictionary<(Type, double), Lazy> LookupTables = new();
+
+ ///
+ /// Lazily creates and stores a companding lookup table using the given function and modifier.
+ ///
+ /// The type of companding function.
+ /// The companding function.
+ /// A modifier to pass to the function.
+ /// The array.
+ public static Lazy GetLookupTable(Func compandingFunction, double modifier = 0)
+ => LookupTables.GetOrAdd((typeof(T), modifier), args => new(() => CreateLookupTableImpl(compandingFunction, args.Item2), true));
+
+ ///
+ /// Creates a companding lookup table using the given function.
+ ///
+ /// The companding function.
+ /// A modifier to pass to the function.
+ /// The array.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static float[] CreateLookupTableImpl(Func compandingFunction, double modifier = 0)
+ {
+ float[] result = new float[Length];
+
+ for (int i = 0; i < result.Length; i++)
+ {
+ double d = (double)i / Scale;
+ d = compandingFunction(d, modifier);
+ result[i] = (float)d;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Performs the companding operation on the given vectors using the given table.
+ ///
+ /// The span of vectors.
+ /// The lookup table.
+ public static void Compand(Span vectors, float[] table)
+ {
+ DebugGuard.MustBeGreaterThanOrEqualTo(table.Length, Length, nameof(table));
+
+ if (Avx2.IsSupported && vectors.Length >= 2)
+ {
+ CompandAvx2(vectors, table);
+
+ if (Numerics.Modulo2(vectors.Length) != 0)
+ {
+ // Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
+ ref Vector4 last = ref MemoryMarshal.GetReference(vectors[^1..]);
+ last = Compand(last, table);
+ }
+ }
+ else
+ {
+ CompandScalar(vectors, table);
+ }
+ }
+
+ ///
+ /// Performs the companding operation on the given vector using the given table.
+ ///
+ /// The vector.
+ /// The lookup table.
+ /// The
+ public static Vector4 Compand(Vector4 vector, float[] table)
+ {
+ DebugGuard.MustBeGreaterThanOrEqualTo(table.Length, Length, nameof(table));
+
+ Vector4 zero = Vector4.Zero;
+ Vector4 scale = new(Scale);
+
+ Vector4 multiplied = Numerics.Clamp(vector * Scale, zero, scale);
+
+ float f0 = multiplied.X;
+ float f1 = multiplied.Y;
+ float f2 = multiplied.Z;
+
+ uint i0 = (uint)f0;
+ uint i1 = (uint)f1;
+ uint i2 = (uint)f2;
+
+ // Alpha is already a linear representation of opacity so we do not want to convert it.
+ vector.X = Numerics.Lerp(table[i0], table[i0 + 1], f0 - (int)i0);
+ vector.Y = Numerics.Lerp(table[i1], table[i1 + 1], f1 - (int)i1);
+ vector.Z = Numerics.Lerp(table[i2], table[i2 + 1], f2 - (int)i2);
+
+ return vector;
+ }
+
+ private static unsafe void CompandAvx2(Span vectors, float[] table)
+ {
+ fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table))
+ {
+ Vector256 scale = Vector256.Create((float)Scale);
+ Vector256 zero = Vector256.Zero;
+ Vector256 offset = Vector256.Create(1);
+
+ // Divide by 2 as 4 elements per Vector4 and 8 per Vector256
+ ref Vector256 vectorsBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors));
+ ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length / 2u);
+
+ while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast))
+ {
+ Vector256 multiplied = Avx.Multiply(scale, vectorsBase);
+ multiplied = Avx.Min(Avx.Max(zero, multiplied), scale);
+
+ Vector256 truncated = Avx.ConvertToVector256Int32WithTruncation(multiplied);
+ Vector256 truncatedF = Avx.ConvertToVector256Single(truncated);
+
+ Vector256 low = Avx2.GatherVector256(tablePointer, truncated, sizeof(float));
+ Vector256 high = Avx2.GatherVector256(tablePointer, Avx2.Add(truncated, offset), sizeof(float));
+
+ // Alpha is already a linear representation of opacity so we do not want to convert it.
+ Vector256 companded = Numerics.Lerp(low, high, Avx.Subtract(multiplied, truncatedF));
+ vectorsBase = Avx.Blend(companded, vectorsBase, Numerics.BlendAlphaControl);
+ vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
+ }
+ }
+ }
+
+ private static unsafe void CompandScalar(Span vectors, float[] table)
+ {
+ fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table))
+ {
+ Vector4 zero = Vector4.Zero;
+ Vector4 scale = new(Scale);
+ ref Vector4 vectorsBase = ref MemoryMarshal.GetReference(vectors);
+ ref Vector4 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length);
+
+ while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast))
+ {
+ Vector4 multiplied = Numerics.Clamp(vectorsBase * Scale, zero, scale);
+
+ float f0 = multiplied.X;
+ float f1 = multiplied.Y;
+ float f2 = multiplied.Z;
+
+ uint i0 = (uint)f0;
+ uint i1 = (uint)f1;
+ uint i2 = (uint)f2;
+
+ // Alpha is already a linear representation of opacity so we do not want to convert it.
+ vectorsBase.X = Numerics.Lerp(tablePointer[i0], tablePointer[i0 + 1], f0 - (int)i0);
+ vectorsBase.Y = Numerics.Lerp(tablePointer[i1], tablePointer[i1 + 1], f1 - (int)i1);
+ vectorsBase.Z = Numerics.Lerp(tablePointer[i2], tablePointer[i2 + 1], f2 - (int)i2);
+
+ vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs
new file mode 100644
index 0000000000..28e9a46414
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+
+namespace SixLabors.ImageSharp.ColorProfiles.Companding;
+
+///
+/// Implements gamma companding.
+///
+///
+///
+///
+///
+public static class GammaCompanding
+{
+ private static Func CompressFunction => (d, m) => Math.Pow(d, 1 / m);
+
+ private static Func ExpandFunction => Math.Pow;
+
+ ///
+ /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
+ ///
+ /// The span of vectors.
+ /// The gamma value.
+ public static void Compress(Span vectors, double gamma)
+ => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction, gamma).Value);
+
+ ///
+ /// Expands the nonlinear vectors to their linear equivalents with respect to the energy.
+ ///
+ /// The span of vectors.
+ /// The gamma value.
+ public static void Expand(Span vectors, double gamma)
+ => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction, gamma).Value);
+
+ ///
+ /// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
+ ///
+ /// The vector.
+ /// The gamma value.
+ /// The .
+ public static Vector4 Compress(Vector4 vector, double gamma)
+ => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction, gamma).Value);
+
+ ///
+ /// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
+ ///
+ /// The vector.
+ /// The gamma value.
+ /// The .
+ public static Vector4 Expand(Vector4 vector, double gamma)
+ => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction, gamma).Value);
+
+ private class GammaCompandingKey;
+}
diff --git a/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs
new file mode 100644
index 0000000000..79349d1c53
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs
@@ -0,0 +1,71 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+
+namespace SixLabors.ImageSharp.ColorProfiles.Companding;
+
+///
+/// Implements L* companding.
+///
+///
+/// For more info see:
+///
+///
+///
+public static class LCompanding
+{
+ private static Func CompressFunction
+ => (d, _) =>
+ {
+ if (d <= CieConstants.Epsilon)
+ {
+ return (d * CieConstants.Kappa) / 100;
+ }
+
+ return (1.16 * Math.Pow(d, 0.3333333)) - 0.16;
+ };
+
+ private static Func ExpandFunction
+ => (d, _) =>
+ {
+ if (d <= 0.08)
+ {
+ return (100 * d) / CieConstants.Kappa;
+ }
+
+ return Numerics.Pow3(((float)(d + 0.16f)) / 1.16f);
+ };
+
+ ///
+ /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
+ ///
+ /// The span of vectors.
+ public static void Compress(Span vectors)
+ => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value);
+
+ ///
+ /// Expands the nonlinear vectors to their linear equivalents with respect to the energy.
+ ///
+ /// The span of vectors.
+ public static void Expand(Span vectors)
+ => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value);
+
+ ///
+ /// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
+ ///
+ /// The vector.
+ /// The .
+ public static Vector4 Compress(Vector4 vector)
+ => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value);
+
+ ///
+ /// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
+ ///
+ /// The vector.
+ /// The .
+ public static Vector4 Expand(Vector4 vector)
+ => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value);
+
+ private class LCompandingKey;
+}
diff --git a/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs b/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs
new file mode 100644
index 0000000000..9beab3a86c
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs
@@ -0,0 +1,75 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+
+namespace SixLabors.ImageSharp.ColorProfiles.Companding;
+
+///
+/// Implements Rec. 2020 companding function.
+///
+///
+///
+///
+public static class Rec2020Companding
+{
+ private const double Alpha = 1.09929682680944;
+ private const double AlphaMinusOne = Alpha - 1;
+ private const double Beta = 0.018053968510807;
+ private const double InverseBeta = Beta * 4.5;
+ private const double Epsilon = 1 / 0.45;
+
+ private static Func CompressFunction
+ => (d, _) =>
+ {
+ if (d < Beta)
+ {
+ return 4.5 * d;
+ }
+
+ return (Alpha * Math.Pow(d, 0.45)) - AlphaMinusOne;
+ };
+
+ private static Func ExpandFunction
+ => (d, _) =>
+ {
+ if (d < InverseBeta)
+ {
+ return d / 4.5;
+ }
+
+ return Math.Pow((d + AlphaMinusOne) / Alpha, Epsilon);
+ };
+
+ ///
+ /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
+ ///
+ /// The span of vectors.
+ public static void Compress(Span vectors)
+ => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value);
+
+ ///
+ /// Expands the nonlinear vectors to their linear equivalents with respect to the energy.
+ ///
+ /// The span of vectors.
+ public static void Expand(Span vectors)
+ => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value);
+
+ ///
+ /// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
+ ///
+ /// The vector.
+ /// The .
+ public static Vector4 Compress(Vector4 vector)
+ => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value);
+
+ ///
+ /// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
+ ///
+ /// The vector.
+ /// The .
+ public static Vector4 Expand(Vector4 vector)
+ => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value);
+
+ private class Rec2020CompandingKey;
+}
diff --git a/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs b/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs
new file mode 100644
index 0000000000..93a32672c7
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs
@@ -0,0 +1,71 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+
+namespace SixLabors.ImageSharp.ColorProfiles.Companding;
+
+///
+/// Implements the Rec. 709 companding function.
+///
+///
+/// http://en.wikipedia.org/wiki/Rec._709
+///
+public static class Rec709Companding
+{
+ private const double Epsilon = 1 / 0.45;
+
+ private static Func CompressFunction
+ => (d, _) =>
+ {
+ if (d < 0.018)
+ {
+ return 4.5 * d;
+ }
+
+ return (1.099 * Math.Pow(d, 0.45)) - 0.099;
+ };
+
+ private static Func ExpandFunction
+ => (d, _) =>
+ {
+ if (d < 0.081)
+ {
+ return d / 4.5;
+ }
+
+ return Math.Pow((d + 0.099) / 1.099, Epsilon);
+ };
+
+ ///
+ /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
+ ///
+ /// The span of vectors.
+ public static void Compress(Span vectors)
+ => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value);
+
+ ///
+ /// Expands the nonlinear vectors to their linear equivalents with respect to the energy.
+ ///
+ /// The span of vectors.
+ public static void Expand(Span vectors)
+ => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value);
+
+ ///
+ /// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
+ ///
+ /// The vector.
+ /// The .
+ public static Vector4 Compress(Vector4 vector)
+ => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value);
+
+ ///
+ /// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
+ ///
+ /// The vector.
+ /// The .
+ public static Vector4 Expand(Vector4 vector)
+ => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value);
+
+ private class Rec2020CompandingKey;
+}
diff --git a/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs
new file mode 100644
index 0000000000..04e4007a8c
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs
@@ -0,0 +1,71 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+
+namespace SixLabors.ImageSharp.ColorProfiles.Companding;
+
+///
+/// Implements sRGB companding.
+///
+///
+/// For more info see:
+///
+///
+///
+public static class SRgbCompanding
+{
+ private static Func CompressFunction
+ => (d, _) =>
+ {
+ if (d <= (0.04045 / 12.92))
+ {
+ return d * 12.92;
+ }
+
+ return (1.055 * Math.Pow(d, 1.0 / 2.4)) - 0.055;
+ };
+
+ private static Func ExpandFunction
+ => (d, _) =>
+ {
+ if (d <= 0.04045)
+ {
+ return d / 12.92;
+ }
+
+ return Math.Pow((d + 0.055) / 1.055, 2.4);
+ };
+
+ ///
+ /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
+ ///
+ /// The span of vectors.
+ public static void Compress(Span vectors)
+ => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value);
+
+ ///
+ /// Expands the nonlinear vectors to their linear equivalents with respect to the energy.
+ ///
+ /// The span of vectors.
+ public static void Expand(Span vectors)
+ => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value);
+
+ ///
+ /// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
+ ///
+ /// The vector.
+ /// The .
+ public static Vector4 Compress(Vector4 vector)
+ => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value);
+
+ ///
+ /// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
+ ///
+ /// The vector.
+ /// The .
+ public static Vector4 Expand(Vector4 vector)
+ => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value);
+
+ private class SRgbCompandingKey;
+}
diff --git a/src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs b/src/ImageSharp/ColorProfiles/IColorProfile.cs
similarity index 82%
rename from src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs
rename to src/ImageSharp/ColorProfiles/IColorProfile.cs
index e1d4cae355..ac4b425fe2 100644
--- a/src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs
+++ b/src/ImageSharp/ColorProfiles/IColorProfile.cs
@@ -1,24 +1,29 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.ColorProfiles;
+///
+/// Defines the contract for all color profiles.
+///
+public interface IColorProfile
+{
+ ///
+ /// Gets the chromatic adaption white point source.
+ ///
+ /// The .
+ public static abstract ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource();
+}
+
///
/// Defines the contract for all color profiles.
///
/// The type of color profile.
/// The type of color profile connecting space.
-public interface IColorProfile : IEquatable
+public interface IColorProfile : IColorProfile, IEquatable
where TSelf : IColorProfile
where TProfileSpace : struct, IProfileConnectingSpace
{
- ///
- /// Converts the color to the profile connection space.
- ///
- /// The color profile conversion options.
- /// The .
- public TProfileSpace ToProfileConnectingSpace(ColorConversionOptions options);
-
#pragma warning disable CA1000 // Do not declare static members on generic types
///
/// Converts the color from the profile connection space.
@@ -28,14 +33,6 @@ public interface IColorProfile : IEquatable
/// The .
public static abstract TSelf FromProfileConnectingSpace(ColorConversionOptions options, in TProfileSpace source);
- ///
- /// Converts the span of colors to the profile connection space.
- ///
- /// The color profile conversion options.
- /// The color span to convert from.
- /// The color profile span to write the results to.
- public static abstract void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination);
-
///
/// Converts the span of colors from the profile connection space.
///
@@ -43,5 +40,20 @@ public interface IColorProfile : IEquatable
/// The color profile span to convert from.
/// The color span to write the results to.
public static abstract void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination);
+
+ ///
+ /// Converts the color to the profile connection space.
+ ///
+ /// The color profile conversion options.
+ /// The .
+ public TProfileSpace ToProfileConnectingSpace(ColorConversionOptions options);
+
+ ///
+ /// Converts the span of colors to the profile connection space.
+ ///
+ /// The color profile conversion options.
+ /// The color span to convert from.
+ /// The color profile span to write the results to.
+ public static abstract void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination);
#pragma warning restore CA1000 // Do not declare static members on generic types
}
diff --git a/src/ImageSharp/ColorProfiles/Lms.cs b/src/ImageSharp/ColorProfiles/Lms.cs
index e11c1ca87a..cb060185df 100644
--- a/src/ImageSharp/ColorProfiles/Lms.cs
+++ b/src/ImageSharp/ColorProfiles/Lms.cs
@@ -133,4 +133,7 @@ internal readonly struct Lms : IColorProfile
destination[i] = lms.ToProfileConnectingSpace(options);
}
}
+
+ ///
+ public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint;
}
diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs
new file mode 100644
index 0000000000..2669d2c963
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/Rgb.cs
@@ -0,0 +1,246 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces;
+
+namespace SixLabors.ImageSharp.ColorProfiles;
+
+internal readonly struct Rgb : IProfileConnectingSpace
+{
+ private static readonly Vector3 Min = Vector3.Zero;
+ private static readonly Vector3 Max = Vector3.One;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The red component ranging between 0 and 1.
+ /// The green component ranging between 0 and 1.
+ /// The blue component ranging between 0 and 1.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Rgb(float r, float g, float b)
+ : this(new Vector3(r, g, b))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The vector representing the r, g, b components.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private Rgb(Vector3 source)
+ {
+ source = Vector3.Clamp(source, Min, Max);
+ this.R = source.X;
+ this.G = source.Y;
+ this.B = source.Z;
+ }
+
+ ///
+ /// Gets the red component.
+ /// A value usually ranging between 0 and 1.
+ ///
+ public readonly float R { get; }
+
+ ///
+ /// Gets the green component.
+ /// A value usually ranging between 0 and 1.
+ ///
+ public readonly float G { get; }
+
+ ///
+ /// Gets the blue component.
+ /// A value usually ranging between 0 and 1.
+ ///
+ public readonly float B { get; }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ /// The on the left side of the operand.
+ /// The on the right side of the operand.
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(Rgb left, Rgb right) => left.Equals(right);
+
+ ///
+ /// Compares two objects for inequality.
+ ///
+ /// The on the left side of the operand.
+ /// The on the right side of the operand.
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(Rgb left, Rgb right) => !left.Equals(right);
+
+ ///
+ public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
+
+ ///
+ public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})");
+
+ ///
+ public override bool Equals(object? obj) => obj is Rgb other && this.Equals(other);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Equals(Rgb other)
+ => this.R.Equals(other.R)
+ && this.G.Equals(other.G)
+ && this.B.Equals(other.B);
+
+ ///
+ public static Rgb FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source)
+ {
+ // Convert to linear rgb then compress.
+ Rgb linear = new(Vector3.Transform(source.ToVector3(), GetCieXyzToRgbMatrix(options.TargetRgbWorkingSpace)));
+ return FromScaledVector4(options.TargetRgbWorkingSpace.Compress(linear.ToScaledVector4()));
+ }
+
+ ///
+ public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination)
+ {
+ Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
+
+ Matrix4x4 matrix = GetCieXyzToRgbMatrix(options.TargetRgbWorkingSpace);
+ for (int i = 0; i < source.Length; i++)
+ {
+ // Convert to linear rgb then compress.
+ Rgb linear = new(Vector3.Transform(source[i].ToVector3(), matrix));
+ Vector4 nonlinear = options.TargetRgbWorkingSpace.Compress(linear.ToScaledVector4());
+ destination[i] = FromScaledVector4(nonlinear);
+ }
+ }
+
+ ///
+ public CieXyz ToProfileConnectingSpace(ColorConversionOptions options)
+ {
+ // First expand to linear rgb
+ Rgb linear = FromScaledVector4(options.RgbWorkingSpace.Expand(this.ToScaledVector4()));
+
+ // Then convert to xyz
+ return new CieXyz(Vector3.Transform(linear.ToScaledVector3(), GetRgbToCieXyzMatrix(options.RgbWorkingSpace)));
+ }
+
+ ///
+ public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination)
+ {
+ Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
+
+ Matrix4x4 matrix = GetRgbToCieXyzMatrix(options.RgbWorkingSpace);
+ 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()));
+
+ // Then convert to xyz
+ destination[i] = new CieXyz(Vector3.Transform(linear.ToScaledVector3(), matrix));
+ }
+ }
+
+ ///
+ public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.RgbWorkingSpace;
+
+ ///
+ /// Initializes the pixel instance from a generic scaled .
+ ///
+ /// The vector to load the pixel from.
+ /// The .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Rgb FromScaledVector3(Vector3 source) => new(source);
+
+ ///
+ /// Initializes the pixel instance from a generic scaled .
+ ///
+ /// The vector to load the pixel from.
+ /// The .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Rgb FromScaledVector4(Vector4 source) => new(source.X, source.Y, source.Z);
+
+ ///
+ /// Expands the color into a generic ("scaled") representation
+ /// with values scaled and clamped between 0 and 1.
+ /// The vector components are typically expanded in least to greatest significance order.
+ ///
+ /// The .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector3 ToScaledVector3() => new(this.R, this.G, this.B);
+
+ ///
+ /// Expands the color into a generic ("scaled") representation
+ /// with values scaled and clamped between 0 and 1.
+ /// The vector components are typically expanded in least to greatest significance order.
+ ///
+ /// The .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector4 ToScaledVector4() => new(this.R, this.G, this.B, 1f);
+
+ 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.ToVector3(), inverseXyzMatrix);
+
+ // Use transposed Rows/Columns
+ // TODO: Is there a built in method for this multiplication?
+ return new Matrix4x4
+ {
+ M11 = vector.X * mXr,
+ M21 = vector.Y * mXg,
+ M31 = vector.Z * mXb,
+ M12 = vector.X * 1,
+ M22 = vector.Y * 1,
+ M32 = vector.Z * 1,
+ M13 = vector.X * mZr,
+ M23 = vector.Y * mZg,
+ M33 = vector.Z * mZb,
+ M44 = 1F
+ };
+ }
+}
diff --git a/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs
new file mode 100644
index 0000000000..90a5a57f9c
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs
@@ -0,0 +1,82 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces;
+
+namespace SixLabors.ImageSharp.ColorProfiles;
+
+///
+/// Represents the chromaticity coordinates of RGB primaries.
+/// One of the specifiers of .
+///
+public readonly struct RgbPrimariesChromaticityCoordinates : IEquatable
+{
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The chromaticity coordinates of the red channel.
+ /// The chromaticity coordinates of the green channel.
+ /// The chromaticity coordinates of the blue channel.
+ public RgbPrimariesChromaticityCoordinates(CieXyChromaticityCoordinates r, CieXyChromaticityCoordinates g, CieXyChromaticityCoordinates b)
+ {
+ this.R = r;
+ this.G = g;
+ this.B = b;
+ }
+
+ ///
+ /// Gets the chromaticity coordinates of the red channel.
+ ///
+ public CieXyChromaticityCoordinates R { get; }
+
+ ///
+ /// Gets the chromaticity coordinates of the green channel.
+ ///
+ public CieXyChromaticityCoordinates G { get; }
+
+ ///
+ /// Gets the chromaticity coordinates of the blue channel.
+ ///
+ public CieXyChromaticityCoordinates B { get; }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right)
+ => left.Equals(right);
+
+ ///
+ /// Compares two objects for inequality
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right)
+ => !left.Equals(right);
+
+ ///
+ public override bool Equals(object? obj)
+ => obj is RgbPrimariesChromaticityCoordinates other && this.Equals(other);
+
+ ///
+ public bool Equals(RgbPrimariesChromaticityCoordinates other)
+ => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B);
+
+ ///
+ public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
+}
diff --git a/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs b/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs
new file mode 100644
index 0000000000..c0d7f50746
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs
@@ -0,0 +1,114 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.ColorProfiles;
+using SixLabors.ImageSharp.ColorProfiles.Companding;
+using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces;
+
+namespace SixLabors.ColorProfiles;
+
+///
+/// Chromaticity coordinates based on:
+///
+public static class RgbWorkingSpaces
+{
+ ///
+ /// sRgb working space.
+ ///
+ ///
+ /// Uses proper companding function, according to:
+ ///
+ ///
+ public static readonly RgbWorkingSpace SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
+
+ ///
+ /// Simplified sRgb working space (uses gamma companding instead of ).
+ /// See also .
+ ///
+ public static readonly RgbWorkingSpace SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
+
+ ///
+ /// Rec. 709 (ITU-R Recommendation BT.709) working space.
+ ///
+ public static readonly RgbWorkingSpace Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F)));
+
+ ///
+ /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space.
+ ///
+ public static readonly RgbWorkingSpace Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F)));
+
+ ///
+ /// ECI Rgb v2 working space.
+ ///
+ public static readonly RgbWorkingSpace ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F)));
+
+ ///
+ /// Adobe Rgb (1998) working space.
+ ///
+ public static readonly RgbWorkingSpace AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
+
+ ///
+ /// Apple sRgb working space.
+ ///
+ public static readonly RgbWorkingSpace ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F)));
+
+ ///
+ /// Best Rgb working space.
+ ///
+ public static readonly RgbWorkingSpace BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F)));
+
+ ///
+ /// Beta Rgb working space.
+ ///
+ public static readonly RgbWorkingSpace BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F)));
+
+ ///
+ /// Bruce Rgb working space.
+ ///
+ public static readonly RgbWorkingSpace BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
+
+ ///
+ /// CIE Rgb working space.
+ ///
+ public static readonly RgbWorkingSpace CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F)));
+
+ ///
+ /// ColorMatch Rgb working space.
+ ///
+ public static readonly RgbWorkingSpace ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F)));
+
+ ///
+ /// Don Rgb 4 working space.
+ ///
+ public static readonly RgbWorkingSpace DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F)));
+
+ ///
+ /// Ekta Space PS5 working space.
+ ///
+ public static readonly RgbWorkingSpace EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F)));
+
+ ///
+ /// NTSC Rgb working space.
+ ///
+ public static readonly RgbWorkingSpace NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F)));
+
+ ///
+ /// PAL/SECAM Rgb working space.
+ ///
+ public static readonly RgbWorkingSpace PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F)));
+
+ ///
+ /// ProPhoto Rgb working space.
+ ///
+ public static readonly RgbWorkingSpace ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F)));
+
+ ///
+ /// SMPTE-C Rgb working space.
+ ///
+ public static readonly RgbWorkingSpace SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F)));
+
+ ///
+ /// Wide Gamut Rgb working space.
+ ///
+ public static readonly RgbWorkingSpace WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F)));
+}
diff --git a/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs
index 0505395deb..4719750b7c 100644
--- a/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs
+++ b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs
@@ -19,16 +19,25 @@ public static class VonKriesChromaticAdaptation
///
/// Performs a linear transformation of a source color in to the destination color.
///
+ /// The type of color profile to convert from.
+ /// The type of color profile to convert to.
/// Doesn't crop the resulting color space coordinates (e.g. allows negative values for XYZ coordinates).
/// The color profile conversion options.
/// The source color.
/// The
- public static CieXyz Transform(ColorConversionOptions options, in CieXyz source)
+ public static CieXyz Transform(ColorConversionOptions options, in CieXyz source)
+ where TFrom : struct, IColorProfile
+ where TTo : struct, IColorProfile
{
- CieXyz sourceWhitePoint = options.WhitePoint;
- CieXyz destinationWhitePoint = options.TargetWhitePoint;
+ CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint
+ ? options.WhitePoint
+ : options.RgbWorkingSpace.WhitePoint;
- if (sourceWhitePoint.Equals(destinationWhitePoint))
+ CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint
+ ? options.TargetWhitePoint
+ : options.TargetRgbWorkingSpace.WhitePoint;
+
+ if (sourceWhitePoint.Equals(targetWhitePoint))
{
return new(source.X, source.Y, source.Z);
}
@@ -37,7 +46,7 @@ public static class VonKriesChromaticAdaptation
Vector3 sourceColorLms = Vector3.Transform(source.ToVector3(), matrix);
Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix);
- Vector3 targetWhitePointLms = Vector3.Transform(destinationWhitePoint.ToVector3(), matrix);
+ Vector3 targetWhitePointLms = Vector3.Transform(targetWhitePoint.ToVector3(), matrix);
Vector3 vector = targetWhitePointLms / sourceWhitePointLms;
Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms);
@@ -49,19 +58,28 @@ public static class VonKriesChromaticAdaptation
///
/// Performs a bulk linear transformation of a source color in to the destination color.
///
+ /// The type of color profile to convert from.
+ /// The type of color profile to convert to.
/// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates).
/// The color profile conversion options.
/// The span to the source colors.
/// The span to the destination colors.
- public static void Transform(ColorConversionOptions options, ReadOnlySpan source, Span destination)
+ public static void Transform(ColorConversionOptions options, ReadOnlySpan source, Span destination)
+ where TFrom : struct, IColorProfile
+ where TTo : struct, IColorProfile
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
- CieXyz sourceWhitePoint = options.WhitePoint;
- CieXyz destinationWhitePoint = options.TargetWhitePoint;
+ CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint
+ ? options.WhitePoint
+ : options.RgbWorkingSpace.WhitePoint;
+
+ CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint
+ ? options.TargetWhitePoint
+ : options.TargetRgbWorkingSpace.WhitePoint;
- if (sourceWhitePoint.Equals(destinationWhitePoint))
+ if (sourceWhitePoint.Equals(targetWhitePoint))
{
source.CopyTo(destination[..count]);
return;
@@ -73,16 +91,18 @@ public static class VonKriesChromaticAdaptation
ref CieXyz sourceBase = ref MemoryMarshal.GetReference(source);
ref CieXyz destinationBase = ref MemoryMarshal.GetReference(destination);
+ Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix);
+ Vector3 targetWhitePointLms = Vector3.Transform(targetWhitePoint.ToVector3(), matrix);
+
+ Vector3 vector = targetWhitePointLms / sourceWhitePointLms;
+
for (nuint i = 0; i < (uint)count; i++)
{
ref CieXyz sp = ref Unsafe.Add(ref sourceBase, i);
ref CieXyz dp = ref Unsafe.Add(ref destinationBase, i);
Vector3 sourceColorLms = Vector3.Transform(sp.ToVector3(), matrix);
- Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix);
- Vector3 targetWhitePointLms = Vector3.Transform(destinationWhitePoint.ToVector3(), matrix);
- Vector3 vector = targetWhitePointLms / sourceWhitePointLms;
Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms);
dp = new CieXyz(Vector3.Transform(targetColorLms, inverseMatrix));
}
diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs
new file mode 100644
index 0000000000..91fa426241
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs
@@ -0,0 +1,68 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+using SixLabors.ImageSharp.ColorProfiles.Companding;
+
+namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces;
+
+///
+/// The gamma working space.
+///
+public sealed class GammaWorkingSpace : RgbWorkingSpace
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The gamma value.
+ /// The reference white point.
+ /// The chromaticity of the rgb primaries.
+ public GammaWorkingSpace(float gamma, CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
+ : base(referenceWhite, chromaticityCoordinates) => this.Gamma = gamma;
+
+ ///
+ /// Gets the gamma value.
+ ///
+ public float Gamma { get; }
+
+ ///
+ public override void Compress(Span vectors) => GammaCompanding.Compress(vectors, this.Gamma);
+
+ ///
+ public override void Expand(Span vectors) => GammaCompanding.Expand(vectors, this.Gamma);
+
+ ///
+ public override Vector4 Compress(Vector4 vector) => GammaCompanding.Compress(vector, this.Gamma);
+
+ ///
+ public override Vector4 Expand(Vector4 vector) => GammaCompanding.Expand(vector, this.Gamma);
+
+ ///
+ public override bool Equals(object? obj)
+ {
+ if (obj is null)
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, obj))
+ {
+ return true;
+ }
+
+ if (obj is GammaWorkingSpace other)
+ {
+ return this.Gamma.Equals(other.Gamma)
+ && this.WhitePoint.Equals(other.WhitePoint)
+ && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates);
+ }
+
+ return false;
+ }
+
+ ///
+ public override int GetHashCode() => HashCode.Combine(
+ this.WhitePoint,
+ this.ChromaticityCoordinates,
+ this.Gamma);
+}
diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/LWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/LWorkingSpace.cs
new file mode 100644
index 0000000000..77dc2c06d9
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/LWorkingSpace.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+using SixLabors.ImageSharp.ColorProfiles.Companding;
+
+namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces;
+
+///
+/// L* working space.
+///
+public sealed class LWorkingSpace : RgbWorkingSpace
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The reference white point.
+ /// The chromaticity of the rgb primaries.
+ public LWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
+ : base(referenceWhite, chromaticityCoordinates)
+ {
+ }
+
+ ///
+ public override void Compress(Span vectors) => LCompanding.Compress(vectors);
+
+ ///
+ public override void Expand(Span vectors) => LCompanding.Expand(vectors);
+
+ ///
+ public override Vector4 Compress(Vector4 vector) => LCompanding.Compress(vector);
+
+ ///
+ public override Vector4 Expand(Vector4 vector) => LCompanding.Expand(vector);
+}
diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec2020WorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec2020WorkingSpace.cs
new file mode 100644
index 0000000000..970d103029
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec2020WorkingSpace.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+using SixLabors.ImageSharp.ColorProfiles.Companding;
+
+namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces;
+
+///
+/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space.
+///
+public sealed class Rec2020WorkingSpace : RgbWorkingSpace
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The reference white point.
+ /// The chromaticity of the rgb primaries.
+ public Rec2020WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
+ : base(referenceWhite, chromaticityCoordinates)
+ {
+ }
+
+ ///
+ public override void Compress(Span vectors) => Rec2020Companding.Compress(vectors);
+
+ ///
+ public override void Expand(Span vectors) => Rec2020Companding.Expand(vectors);
+
+ ///
+ public override Vector4 Compress(Vector4 vector) => Rec2020Companding.Compress(vector);
+
+ ///
+ public override Vector4 Expand(Vector4 vector) => Rec2020Companding.Expand(vector);
+}
diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec709WorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec709WorkingSpace.cs
new file mode 100644
index 0000000000..011da58077
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec709WorkingSpace.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+using SixLabors.ImageSharp.ColorProfiles.Companding;
+
+namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces;
+
+///
+/// Rec. 709 (ITU-R Recommendation BT.709) working space.
+///
+public sealed class Rec709WorkingSpace : RgbWorkingSpace
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The reference white point.
+ /// The chromaticity of the rgb primaries.
+ public Rec709WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
+ : base(referenceWhite, chromaticityCoordinates)
+ {
+ }
+
+ ///
+ public override void Compress(Span vectors) => Rec709Companding.Compress(vectors);
+
+ ///
+ public override void Expand(Span vectors) => Rec709Companding.Expand(vectors);
+
+ ///
+ public override Vector4 Compress(Vector4 vector) => Rec709Companding.Compress(vector);
+
+ ///
+ public override Vector4 Expand(Vector4 vector) => Rec709Companding.Expand(vector);
+}
diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs
new file mode 100644
index 0000000000..97069b856b
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs
@@ -0,0 +1,85 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+
+namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces;
+
+///
+/// Base class for all implementations of .
+///
+public abstract class RgbWorkingSpace
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The reference white point.
+ /// The chromaticity of the rgb primaries.
+ protected RgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
+ {
+ this.WhitePoint = referenceWhite;
+ this.ChromaticityCoordinates = chromaticityCoordinates;
+ }
+
+ ///
+ /// Gets the reference white point
+ ///
+ public CieXyz WhitePoint { get; }
+
+ ///
+ /// Gets the chromaticity of the rgb primaries.
+ ///
+ public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; }
+
+ ///
+ /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
+ ///
+ /// The span of vectors.
+ public abstract void Compress(Span vectors);
+
+ ///
+ /// Expands the nonlinear vectors to their linear equivalents with respect to the energy.
+ ///
+ /// The span of vectors.
+ public abstract void Expand(Span vectors);
+
+ ///
+ /// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
+ ///
+ /// The vector.
+ /// The .
+ public abstract Vector4 Compress(Vector4 vector);
+
+ ///
+ /// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
+ ///
+ /// The vector.
+ /// The .
+ public abstract Vector4 Expand(Vector4 vector);
+
+ ///
+ public override bool Equals(object? obj)
+ {
+ if (obj is null)
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, obj))
+ {
+ return true;
+ }
+
+ if (obj is RgbWorkingSpace other)
+ {
+ return this.WhitePoint.Equals(other.WhitePoint)
+ && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates);
+ }
+
+ return false;
+ }
+
+ ///
+ public override int GetHashCode()
+ => HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates);
+}
diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/SRgbWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/SRgbWorkingSpace.cs
new file mode 100644
index 0000000000..b88e6c8983
--- /dev/null
+++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/SRgbWorkingSpace.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+using SixLabors.ImageSharp.ColorProfiles.Companding;
+
+namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces;
+
+///
+/// The sRgb working space.
+///
+public sealed class SRgbWorkingSpace : RgbWorkingSpace
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The reference white point.
+ /// The chromaticity of the rgb primaries.
+ public SRgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates)
+ : base(referenceWhite, chromaticityCoordinates)
+ {
+ }
+
+ ///
+ public override void Compress(Span vectors) => SRgbCompanding.Compress(vectors);
+
+ ///
+ public override void Expand(Span vectors) => SRgbCompanding.Expand(vectors);
+
+ ///
+ public override Vector4 Compress(Vector4 vector) => SRgbCompanding.Compress(vector);
+
+ ///
+ public override Vector4 Expand(Vector4 vector) => SRgbCompanding.Expand(vector);
+}