mirror of https://github.com/SixLabors/ImageSharp
27 changed files with 1445 additions and 36 deletions
@ -0,0 +1,20 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.ColorProfiles; |
|||
|
|||
/// <summary>
|
|||
/// Enumerate the possible sources of the white point used in chromatic adaptation.
|
|||
/// </summary>
|
|||
public enum ChromaticAdaptionWhitePointSource |
|||
{ |
|||
/// <summary>
|
|||
/// The white point of the source color space.
|
|||
/// </summary>
|
|||
WhitePoint, |
|||
|
|||
/// <summary>
|
|||
/// The white point of the source working space.
|
|||
/// </summary>
|
|||
RgbWorkingSpace |
|||
} |
|||
@ -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; |
|||
|
|||
/// <summary>
|
|||
/// Represents the coordinates of CIEXY chromaticity space.
|
|||
/// </summary>
|
|||
public readonly struct CieXyChromaticityCoordinates : IEquatable<CieXyChromaticityCoordinates> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieXyChromaticityCoordinates"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="x">Chromaticity coordinate x (usually from 0 to 1)</param>
|
|||
/// <param name="y">Chromaticity coordinate y (usually from 0 to 1)</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public CieXyChromaticityCoordinates(float x, float y) |
|||
{ |
|||
this.X = x; |
|||
this.Y = y; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the chromaticity X-coordinate.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Ranges usually from 0 to 1.
|
|||
/// </remarks>
|
|||
public readonly float X { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the chromaticity Y-coordinate
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Ranges usually from 0 to 1.
|
|||
/// </remarks>
|
|||
public readonly float Y { get; } |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.</param>
|
|||
/// <param name="right">The <see cref="CieXyChromaticityCoordinates"/> on the right side of the operand.</param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) |
|||
=> left.Equals(right); |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for inequality
|
|||
/// </summary>
|
|||
/// <param name="left">The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.</param>
|
|||
/// <param name="right">The <see cref="CieXyChromaticityCoordinates"/> on the right side of the operand.</param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) |
|||
=> !left.Equals(right); |
|||
|
|||
/// <inheritdoc />
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public override int GetHashCode() |
|||
=> HashCode.Combine(this.X, this.Y); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
=> FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})"); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object? obj) |
|||
=> obj is CieXyChromaticityCoordinates other && this.Equals(other); |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public bool Equals(CieXyChromaticityCoordinates other) |
|||
=> this.X.Equals(other.X) && this.Y.Equals(other.Y); |
|||
} |
|||
@ -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; |
|||
|
|||
/// <summary>
|
|||
/// Companding utilities that allow the accelerated compression-expansion of color channels.
|
|||
/// </summary>
|
|||
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<float[]>> LookupTables = new(); |
|||
|
|||
/// <summary>
|
|||
/// Lazily creates and stores a companding lookup table using the given function and modifier.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of companding function.</typeparam>
|
|||
/// <param name="compandingFunction">The companding function.</param>
|
|||
/// <param name="modifier">A modifier to pass to the function.</param>
|
|||
/// <returns>The <see cref="float"/> array.</returns>
|
|||
public static Lazy<float[]> GetLookupTable<T>(Func<double, double, double> compandingFunction, double modifier = 0) |
|||
=> LookupTables.GetOrAdd((typeof(T), modifier), args => new(() => CreateLookupTableImpl(compandingFunction, args.Item2), true)); |
|||
|
|||
/// <summary>
|
|||
/// Creates a companding lookup table using the given function.
|
|||
/// </summary>
|
|||
/// <param name="compandingFunction">The companding function.</param>
|
|||
/// <param name="modifier">A modifier to pass to the function.</param>
|
|||
/// <returns>The <see cref="float"/> array.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static float[] CreateLookupTableImpl(Func<double, double, double> 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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Performs the companding operation on the given vectors using the given table.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
/// <param name="table">The lookup table.</param>
|
|||
public static void Compand(Span<Vector4> 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); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Performs the companding operation on the given vector using the given table.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <param name="table">The lookup table.</param>
|
|||
/// <returns>The <see cref="Vector4"/></returns>
|
|||
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<Vector4> vectors, float[] table) |
|||
{ |
|||
fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) |
|||
{ |
|||
Vector256<float> scale = Vector256.Create((float)Scale); |
|||
Vector256<float> zero = Vector256<float>.Zero; |
|||
Vector256<int> offset = Vector256.Create(1); |
|||
|
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
|
|||
ref Vector256<float> vectorsBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(vectors)); |
|||
ref Vector256<float> vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length / 2u); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) |
|||
{ |
|||
Vector256<float> multiplied = Avx.Multiply(scale, vectorsBase); |
|||
multiplied = Avx.Min(Avx.Max(zero, multiplied), scale); |
|||
|
|||
Vector256<int> truncated = Avx.ConvertToVector256Int32WithTruncation(multiplied); |
|||
Vector256<float> truncatedF = Avx.ConvertToVector256Single(truncated); |
|||
|
|||
Vector256<float> low = Avx2.GatherVector256(tablePointer, truncated, sizeof(float)); |
|||
Vector256<float> 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<float> 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<Vector4> 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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
|
|||
namespace SixLabors.ImageSharp.ColorProfiles.Companding; |
|||
|
|||
/// <summary>
|
|||
/// Implements gamma companding.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
|
|||
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
|
|||
/// </remarks>
|
|||
public static class GammaCompanding |
|||
{ |
|||
private static Func<double, double, double> CompressFunction => (d, m) => Math.Pow(d, 1 / m); |
|||
|
|||
private static Func<double, double, double> ExpandFunction => Math.Pow; |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
/// <param name="gamma">The gamma value.</param>
|
|||
public static void Compress(Span<Vector4> vectors, double gamma) |
|||
=> CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable<GammaCompandingKey>(CompressFunction, gamma).Value); |
|||
|
|||
/// <summary>
|
|||
/// Expands the nonlinear vectors to their linear equivalents with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
/// <param name="gamma">The gamma value.</param>
|
|||
public static void Expand(Span<Vector4> vectors, double gamma) |
|||
=> CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable<GammaCompandingKey>(ExpandFunction, gamma).Value); |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <param name="gamma">The gamma value.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
public static Vector4 Compress(Vector4 vector, double gamma) |
|||
=> CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable<GammaCompandingKey>(CompressFunction, gamma).Value); |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <param name="gamma">The gamma value.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
public static Vector4 Expand(Vector4 vector, double gamma) |
|||
=> CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable<GammaCompandingKey>(ExpandFunction, gamma).Value); |
|||
|
|||
private class GammaCompandingKey; |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
|
|||
namespace SixLabors.ImageSharp.ColorProfiles.Companding; |
|||
|
|||
/// <summary>
|
|||
/// Implements L* companding.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// For more info see:
|
|||
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
|
|||
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
|
|||
/// </remarks>
|
|||
public static class LCompanding |
|||
{ |
|||
private static Func<double, double, double> CompressFunction |
|||
=> (d, _) => |
|||
{ |
|||
if (d <= CieConstants.Epsilon) |
|||
{ |
|||
return (d * CieConstants.Kappa) / 100; |
|||
} |
|||
|
|||
return (1.16 * Math.Pow(d, 0.3333333)) - 0.16; |
|||
}; |
|||
|
|||
private static Func<double, double, double> ExpandFunction |
|||
=> (d, _) => |
|||
{ |
|||
if (d <= 0.08) |
|||
{ |
|||
return (100 * d) / CieConstants.Kappa; |
|||
} |
|||
|
|||
return Numerics.Pow3(((float)(d + 0.16f)) / 1.16f); |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
public static void Compress(Span<Vector4> vectors) |
|||
=> CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable<LCompandingKey>(CompressFunction).Value); |
|||
|
|||
/// <summary>
|
|||
/// Expands the nonlinear vectors to their linear equivalents with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
public static void Expand(Span<Vector4> vectors) |
|||
=> CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable<LCompandingKey>(ExpandFunction).Value); |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
public static Vector4 Compress(Vector4 vector) |
|||
=> CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable<LCompandingKey>(CompressFunction).Value); |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
public static Vector4 Expand(Vector4 vector) |
|||
=> CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable<LCompandingKey>(ExpandFunction).Value); |
|||
|
|||
private class LCompandingKey; |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
|
|||
namespace SixLabors.ImageSharp.ColorProfiles.Companding; |
|||
|
|||
/// <summary>
|
|||
/// Implements Rec. 2020 companding function.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <see href="http://en.wikipedia.org/wiki/Rec._2020"/>
|
|||
/// </remarks>
|
|||
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<double, double, double> CompressFunction |
|||
=> (d, _) => |
|||
{ |
|||
if (d < Beta) |
|||
{ |
|||
return 4.5 * d; |
|||
} |
|||
|
|||
return (Alpha * Math.Pow(d, 0.45)) - AlphaMinusOne; |
|||
}; |
|||
|
|||
private static Func<double, double, double> ExpandFunction |
|||
=> (d, _) => |
|||
{ |
|||
if (d < InverseBeta) |
|||
{ |
|||
return d / 4.5; |
|||
} |
|||
|
|||
return Math.Pow((d + AlphaMinusOne) / Alpha, Epsilon); |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
public static void Compress(Span<Vector4> vectors) |
|||
=> CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable<Rec2020CompandingKey>(CompressFunction).Value); |
|||
|
|||
/// <summary>
|
|||
/// Expands the nonlinear vectors to their linear equivalents with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
public static void Expand(Span<Vector4> vectors) |
|||
=> CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable<Rec2020CompandingKey>(ExpandFunction).Value); |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
public static Vector4 Compress(Vector4 vector) |
|||
=> CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable<Rec2020CompandingKey>(CompressFunction).Value); |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
public static Vector4 Expand(Vector4 vector) |
|||
=> CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable<Rec2020CompandingKey>(ExpandFunction).Value); |
|||
|
|||
private class Rec2020CompandingKey; |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
|
|||
namespace SixLabors.ImageSharp.ColorProfiles.Companding; |
|||
|
|||
/// <summary>
|
|||
/// Implements the Rec. 709 companding function.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// http://en.wikipedia.org/wiki/Rec._709
|
|||
/// </remarks>
|
|||
public static class Rec709Companding |
|||
{ |
|||
private const double Epsilon = 1 / 0.45; |
|||
|
|||
private static Func<double, double, double> CompressFunction |
|||
=> (d, _) => |
|||
{ |
|||
if (d < 0.018) |
|||
{ |
|||
return 4.5 * d; |
|||
} |
|||
|
|||
return (1.099 * Math.Pow(d, 0.45)) - 0.099; |
|||
}; |
|||
|
|||
private static Func<double, double, double> ExpandFunction |
|||
=> (d, _) => |
|||
{ |
|||
if (d < 0.081) |
|||
{ |
|||
return d / 4.5; |
|||
} |
|||
|
|||
return Math.Pow((d + 0.099) / 1.099, Epsilon); |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
public static void Compress(Span<Vector4> vectors) |
|||
=> CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable<Rec2020CompandingKey>(CompressFunction).Value); |
|||
|
|||
/// <summary>
|
|||
/// Expands the nonlinear vectors to their linear equivalents with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
public static void Expand(Span<Vector4> vectors) |
|||
=> CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable<Rec2020CompandingKey>(ExpandFunction).Value); |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
public static Vector4 Compress(Vector4 vector) |
|||
=> CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable<Rec2020CompandingKey>(CompressFunction).Value); |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
public static Vector4 Expand(Vector4 vector) |
|||
=> CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable<Rec2020CompandingKey>(ExpandFunction).Value); |
|||
|
|||
private class Rec2020CompandingKey; |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
|
|||
namespace SixLabors.ImageSharp.ColorProfiles.Companding; |
|||
|
|||
/// <summary>
|
|||
/// Implements sRGB companding.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// For more info see:
|
|||
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
|
|||
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
|
|||
/// </remarks>
|
|||
public static class SRgbCompanding |
|||
{ |
|||
private static Func<double, double, double> 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<double, double, double> ExpandFunction |
|||
=> (d, _) => |
|||
{ |
|||
if (d <= 0.04045) |
|||
{ |
|||
return d / 12.92; |
|||
} |
|||
|
|||
return Math.Pow((d + 0.055) / 1.055, 2.4); |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
public static void Compress(Span<Vector4> vectors) |
|||
=> CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable<SRgbCompandingKey>(CompressFunction).Value); |
|||
|
|||
/// <summary>
|
|||
/// Expands the nonlinear vectors to their linear equivalents with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
public static void Expand(Span<Vector4> vectors) |
|||
=> CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable<SRgbCompandingKey>(ExpandFunction).Value); |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
public static Vector4 Compress(Vector4 vector) |
|||
=> CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable<SRgbCompandingKey>(CompressFunction).Value); |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
public static Vector4 Expand(Vector4 vector) |
|||
=> CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable<SRgbCompandingKey>(ExpandFunction).Value); |
|||
|
|||
private class SRgbCompandingKey; |
|||
} |
|||
@ -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<Rgb, CieXyz> |
|||
{ |
|||
private static readonly Vector3 Min = Vector3.Zero; |
|||
private static readonly Vector3 Max = Vector3.One; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgb"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component ranging between 0 and 1.</param>
|
|||
/// <param name="g">The green component ranging between 0 and 1.</param>
|
|||
/// <param name="b">The blue component ranging between 0 and 1.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public Rgb(float r, float g, float b) |
|||
: this(new Vector3(r, g, b)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rgb"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="source">The vector representing the r, g, b components.</param>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the red component.
|
|||
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public readonly float R { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the green component.
|
|||
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public readonly float G { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the blue component.
|
|||
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public readonly float B { get; } |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Rgb"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">The <see cref="Rgb"/> on the left side of the operand.</param>
|
|||
/// <param name="right">The <see cref="Rgb"/> on the right side of the operand.</param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static bool operator ==(Rgb left, Rgb right) => left.Equals(right); |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Rgb"/> objects for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">The <see cref="Rgb"/> on the left side of the operand.</param>
|
|||
/// <param name="right">The <see cref="Rgb"/> on the right side of the operand.</param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static bool operator !=(Rgb left, Rgb right) => !left.Equals(right); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object? obj) => obj is Rgb other && this.Equals(other); |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public bool Equals(Rgb other) |
|||
=> this.R.Equals(other.R) |
|||
&& this.G.Equals(other.G) |
|||
&& this.B.Equals(other.B); |
|||
|
|||
/// <inheritdoc/>
|
|||
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())); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan<CieXyz> source, Span<Rgb> 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); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
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))); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan<Rgb> source, Span<CieXyz> 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)); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; |
|||
|
|||
/// <summary>
|
|||
/// Initializes the pixel instance from a generic scaled <see cref="Vector3"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The vector to load the pixel from.</param>
|
|||
/// <returns>The <see cref="Rgb"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Rgb FromScaledVector3(Vector3 source) => new(source); |
|||
|
|||
/// <summary>
|
|||
/// Initializes the pixel instance from a generic scaled <see cref="Vector4"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The vector to load the pixel from.</param>
|
|||
/// <returns>The <see cref="Rgb"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Rgb FromScaledVector4(Vector4 source) => new(source.X, source.Y, source.Z); |
|||
|
|||
/// <summary>
|
|||
/// Expands the color into a generic ("scaled") <see cref="Vector3"/> 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="Vector3"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public Vector3 ToScaledVector3() => new(this.R, this.G, this.B); |
|||
|
|||
/// <summary>
|
|||
/// Expands the color 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>
|
|||
[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 |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; |
|||
|
|||
namespace SixLabors.ImageSharp.ColorProfiles; |
|||
|
|||
/// <summary>
|
|||
/// Represents the chromaticity coordinates of RGB primaries.
|
|||
/// One of the specifiers of <see cref="RgbWorkingSpace"/>.
|
|||
/// </summary>
|
|||
public readonly struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RgbPrimariesChromaticityCoordinates"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="r">The chromaticity coordinates of the red channel.</param>
|
|||
/// <param name="g">The chromaticity coordinates of the green channel.</param>
|
|||
/// <param name="b">The chromaticity coordinates of the blue channel.</param>
|
|||
public RgbPrimariesChromaticityCoordinates(CieXyChromaticityCoordinates r, CieXyChromaticityCoordinates g, CieXyChromaticityCoordinates b) |
|||
{ |
|||
this.R = r; |
|||
this.G = g; |
|||
this.B = b; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the chromaticity coordinates of the red channel.
|
|||
/// </summary>
|
|||
public CieXyChromaticityCoordinates R { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the chromaticity coordinates of the green channel.
|
|||
/// </summary>
|
|||
public CieXyChromaticityCoordinates G { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the chromaticity coordinates of the blue channel.
|
|||
/// </summary>
|
|||
public CieXyChromaticityCoordinates B { get; } |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="RgbPrimariesChromaticityCoordinates"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="RgbPrimariesChromaticityCoordinates"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="RgbPrimariesChromaticityCoordinates"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator ==(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) |
|||
=> left.Equals(right); |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="RgbPrimariesChromaticityCoordinates"/> objects for inequality
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="RgbPrimariesChromaticityCoordinates"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="RgbPrimariesChromaticityCoordinates"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator !=(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) |
|||
=> !left.Equals(right); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object? obj) |
|||
=> obj is RgbPrimariesChromaticityCoordinates other && this.Equals(other); |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(RgbPrimariesChromaticityCoordinates other) |
|||
=> this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); |
|||
|
|||
/// <inheritdoc />
|
|||
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); |
|||
} |
|||
@ -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; |
|||
|
|||
/// <summary>
|
|||
/// Chromaticity coordinates based on: <see href="http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html"/>
|
|||
/// </summary>
|
|||
public static class RgbWorkingSpaces |
|||
{ |
|||
/// <summary>
|
|||
/// sRgb working space.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Uses proper companding function, according to:
|
|||
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_Rgb_to_XYZ.html"/>
|
|||
/// </remarks>
|
|||
public static readonly RgbWorkingSpace SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); |
|||
|
|||
/// <summary>
|
|||
/// Simplified sRgb working space (uses <see cref="GammaCompanding">gamma companding</see> instead of <see cref="SRgbCompanding"/>).
|
|||
/// See also <see cref="SRgb"/>.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); |
|||
|
|||
/// <summary>
|
|||
/// Rec. 709 (ITU-R Recommendation BT.709) working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); |
|||
|
|||
/// <summary>
|
|||
/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); |
|||
|
|||
/// <summary>
|
|||
/// ECI Rgb v2 working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); |
|||
|
|||
/// <summary>
|
|||
/// Adobe Rgb (1998) working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); |
|||
|
|||
/// <summary>
|
|||
/// Apple sRgb working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); |
|||
|
|||
/// <summary>
|
|||
/// Best Rgb working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); |
|||
|
|||
/// <summary>
|
|||
/// Beta Rgb working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); |
|||
|
|||
/// <summary>
|
|||
/// Bruce Rgb working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); |
|||
|
|||
/// <summary>
|
|||
/// CIE Rgb working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); |
|||
|
|||
/// <summary>
|
|||
/// ColorMatch Rgb working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); |
|||
|
|||
/// <summary>
|
|||
/// Don Rgb 4 working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); |
|||
|
|||
/// <summary>
|
|||
/// Ekta Space PS5 working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); |
|||
|
|||
/// <summary>
|
|||
/// NTSC Rgb working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); |
|||
|
|||
/// <summary>
|
|||
/// PAL/SECAM Rgb working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); |
|||
|
|||
/// <summary>
|
|||
/// ProPhoto Rgb working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); |
|||
|
|||
/// <summary>
|
|||
/// SMPTE-C Rgb working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); |
|||
|
|||
/// <summary>
|
|||
/// Wide Gamut Rgb working space.
|
|||
/// </summary>
|
|||
public static readonly RgbWorkingSpace WideGamutRgb = new 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))); |
|||
} |
|||
@ -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; |
|||
|
|||
/// <summary>
|
|||
/// The gamma working space.
|
|||
/// </summary>
|
|||
public sealed class GammaWorkingSpace : RgbWorkingSpace |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="GammaWorkingSpace" /> class.
|
|||
/// </summary>
|
|||
/// <param name="gamma">The gamma value.</param>
|
|||
/// <param name="referenceWhite">The reference white point.</param>
|
|||
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
|
|||
public GammaWorkingSpace(float gamma, CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) |
|||
: base(referenceWhite, chromaticityCoordinates) => this.Gamma = gamma; |
|||
|
|||
/// <summary>
|
|||
/// Gets the gamma value.
|
|||
/// </summary>
|
|||
public float Gamma { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Compress(Span<Vector4> vectors) => GammaCompanding.Compress(vectors, this.Gamma); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Expand(Span<Vector4> vectors) => GammaCompanding.Expand(vectors, this.Gamma); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Vector4 Compress(Vector4 vector) => GammaCompanding.Compress(vector, this.Gamma); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Vector4 Expand(Vector4 vector) => GammaCompanding.Expand(vector, this.Gamma); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object? obj) |
|||
{ |
|||
if (obj is null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (ReferenceEquals(this, obj)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (obj is GammaWorkingSpace other) |
|||
{ |
|||
return this.Gamma.Equals(other.Gamma) |
|||
&& this.WhitePoint.Equals(other.WhitePoint) |
|||
&& this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() => HashCode.Combine( |
|||
this.WhitePoint, |
|||
this.ChromaticityCoordinates, |
|||
this.Gamma); |
|||
} |
|||
@ -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; |
|||
|
|||
/// <summary>
|
|||
/// L* working space.
|
|||
/// </summary>
|
|||
public sealed class LWorkingSpace : RgbWorkingSpace |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="LWorkingSpace" /> class.
|
|||
/// </summary>
|
|||
/// <param name="referenceWhite">The reference white point.</param>
|
|||
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
|
|||
public LWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) |
|||
: base(referenceWhite, chromaticityCoordinates) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Compress(Span<Vector4> vectors) => LCompanding.Compress(vectors); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Expand(Span<Vector4> vectors) => LCompanding.Expand(vectors); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Vector4 Compress(Vector4 vector) => LCompanding.Compress(vector); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Vector4 Expand(Vector4 vector) => LCompanding.Expand(vector); |
|||
} |
|||
@ -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; |
|||
|
|||
/// <summary>
|
|||
/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space.
|
|||
/// </summary>
|
|||
public sealed class Rec2020WorkingSpace : RgbWorkingSpace |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rec2020WorkingSpace" /> class.
|
|||
/// </summary>
|
|||
/// <param name="referenceWhite">The reference white point.</param>
|
|||
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
|
|||
public Rec2020WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) |
|||
: base(referenceWhite, chromaticityCoordinates) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Compress(Span<Vector4> vectors) => Rec2020Companding.Compress(vectors); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Expand(Span<Vector4> vectors) => Rec2020Companding.Expand(vectors); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Vector4 Compress(Vector4 vector) => Rec2020Companding.Compress(vector); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Vector4 Expand(Vector4 vector) => Rec2020Companding.Expand(vector); |
|||
} |
|||
@ -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; |
|||
|
|||
/// <summary>
|
|||
/// Rec. 709 (ITU-R Recommendation BT.709) working space.
|
|||
/// </summary>
|
|||
public sealed class Rec709WorkingSpace : RgbWorkingSpace |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rec709WorkingSpace" /> class.
|
|||
/// </summary>
|
|||
/// <param name="referenceWhite">The reference white point.</param>
|
|||
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
|
|||
public Rec709WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) |
|||
: base(referenceWhite, chromaticityCoordinates) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Compress(Span<Vector4> vectors) => Rec709Companding.Compress(vectors); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Expand(Span<Vector4> vectors) => Rec709Companding.Expand(vectors); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Vector4 Compress(Vector4 vector) => Rec709Companding.Compress(vector); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Vector4 Expand(Vector4 vector) => Rec709Companding.Expand(vector); |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
|
|||
namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; |
|||
|
|||
/// <summary>
|
|||
/// Base class for all implementations of <see cref="RgbWorkingSpace"/>.
|
|||
/// </summary>
|
|||
public abstract class RgbWorkingSpace |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RgbWorkingSpace"/> class.
|
|||
/// </summary>
|
|||
/// <param name="referenceWhite">The reference white point.</param>
|
|||
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
|
|||
protected RgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) |
|||
{ |
|||
this.WhitePoint = referenceWhite; |
|||
this.ChromaticityCoordinates = chromaticityCoordinates; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the reference white point
|
|||
/// </summary>
|
|||
public CieXyz WhitePoint { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the chromaticity of the rgb primaries.
|
|||
/// </summary>
|
|||
public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vectors to their nonlinear equivalents with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
public abstract void Compress(Span<Vector4> vectors); |
|||
|
|||
/// <summary>
|
|||
/// Expands the nonlinear vectors to their linear equivalents with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors.</param>
|
|||
public abstract void Expand(Span<Vector4> vectors); |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
public abstract Vector4 Compress(Vector4 vector); |
|||
|
|||
/// <summary>
|
|||
/// Compresses the linear vector to its nonlinear equivalent with respect to the energy.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
public abstract Vector4 Expand(Vector4 vector); |
|||
|
|||
/// <inheritdoc/>
|
|||
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; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
=> HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates); |
|||
} |
|||
@ -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; |
|||
|
|||
/// <summary>
|
|||
/// The sRgb working space.
|
|||
/// </summary>
|
|||
public sealed class SRgbWorkingSpace : RgbWorkingSpace |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="SRgbWorkingSpace" /> class.
|
|||
/// </summary>
|
|||
/// <param name="referenceWhite">The reference white point.</param>
|
|||
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
|
|||
public SRgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) |
|||
: base(referenceWhite, chromaticityCoordinates) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Compress(Span<Vector4> vectors) => SRgbCompanding.Compress(vectors); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Expand(Span<Vector4> vectors) => SRgbCompanding.Expand(vectors); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Vector4 Compress(Vector4 vector) => SRgbCompanding.Compress(vector); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Vector4 Expand(Vector4 vector) => SRgbCompanding.Expand(vector); |
|||
} |
|||
Loading…
Reference in new issue