mirror of https://github.com/SixLabors/ImageSharp
24 changed files with 1536 additions and 0 deletions
@ -0,0 +1,187 @@ |
|||
// <copyright file="CieLab.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Represents an CIE LAB 1976 color.
|
|||
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
|
|||
/// </summary>
|
|||
public struct CieLab : IColorVector, IEquatable<CieLab>, IAlmostEquatable<CieLab, float> |
|||
{ |
|||
/// <summary>
|
|||
/// D50 standard illuminant.
|
|||
/// Used when reference white is not specified explicitly.
|
|||
/// </summary>
|
|||
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; |
|||
|
|||
/// <summary>
|
|||
/// Represents a <see cref="CieLab"/> that has L, A, B values set to zero.
|
|||
/// </summary>
|
|||
public static readonly CieLab Empty = default(CieLab); |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private readonly Vector3 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieLab"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="l">The lightness dimension.</param>
|
|||
/// <param name="a">The a (green - magenta) component.</param>
|
|||
/// <param name="b">The b (blue - yellow) component.</param>
|
|||
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
|
|||
public CieLab(float l, float a, float b) |
|||
: this(new Vector3(l, a, b), DefaultWhitePoint) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieLab"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="l">The lightness dimension.</param>
|
|||
/// <param name="a">The a (green - magenta) component.</param>
|
|||
/// <param name="b">The b (blue - yellow) component.</param>
|
|||
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
|
|||
public CieLab(float l, float a, float b, CieXyz whitePoint) |
|||
: this(new Vector3(l, a, b), whitePoint) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieLab"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector representing the l, a, b components.</param>
|
|||
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
|
|||
public CieLab(Vector3 vector) |
|||
: this(vector, DefaultWhitePoint) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieLab"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector representing the l a b components.</param>
|
|||
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
|
|||
public CieLab(Vector3 vector, CieXyz whitePoint) |
|||
: this() |
|||
{ |
|||
this.backingVector = vector; |
|||
this.WhitePoint = whitePoint; |
|||
} |
|||
|
|||
public CieXyz WhitePoint { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the lightness dimension.
|
|||
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
|
|||
/// </summary>
|
|||
public float L => this.backingVector.X; |
|||
|
|||
/// <summary>
|
|||
/// Gets the a color component.
|
|||
/// <remarks>A value ranging from -100 to 100. Negative is green, positive magenta.</remarks>
|
|||
/// </summary>
|
|||
public float A => this.backingVector.Y; |
|||
|
|||
/// <summary>
|
|||
/// Gets the b color component.
|
|||
/// <remarks>A value ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
|
|||
/// </summary>
|
|||
public float B => this.backingVector.Z; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this <see cref="CieLab"/> is empty.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IsEmpty => this.Equals(Empty); |
|||
|
|||
/// <inheritdoc />
|
|||
public Vector3 Vector => this.backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="CieLab"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="CieLab"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="CieLab"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator ==(CieLab left, CieLab right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="CieLab"/> objects for inequality
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="CieLab"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="CieLab"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator !=(CieLab left, CieLab right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return this.backingVector.GetHashCode(); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return "CieLab [Empty]"; |
|||
} |
|||
|
|||
return $"CieLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is CieLab) |
|||
{ |
|||
return this.Equals((CieLab)obj); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(CieLab other) |
|||
{ |
|||
return this.backingVector.Equals(other.backingVector); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool AlmostEquals(CieLab other, float precision) |
|||
{ |
|||
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector); |
|||
|
|||
return result.X <= precision |
|||
&& result.Y <= precision |
|||
&& result.Z <= precision; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,155 @@ |
|||
// <copyright file="CieXyz.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Represents an CIE 1931 color
|
|||
/// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space"/>
|
|||
/// </summary>
|
|||
public struct CieXyz : IColorVector, IEquatable<CieXyz>, IAlmostEquatable<CieXyz, float> |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="CieXyz"/> that has Y, Cb, and Cr values set to zero.
|
|||
/// </summary>
|
|||
public static readonly CieXyz Empty = default(CieXyz); |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private readonly Vector3 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieXyz"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="x">X is a mix (a linear combination) of cone response curves chosen to be nonnegative</param>
|
|||
/// <param name="y">The y luminance component.</param>
|
|||
/// <param name="z">Z is quasi-equal to blue stimulation, or the S cone of the human eye.</param>
|
|||
public CieXyz(float x, float y, float z) |
|||
: this(new Vector3(x, y, z)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieXyz"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector representing the x, y, z components.</param>
|
|||
public CieXyz(Vector3 vector) |
|||
: this() |
|||
{ |
|||
// Not clamping as documentation about this space seems to indicate "usual" ranges
|
|||
this.backingVector = vector; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the Y luminance component.
|
|||
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float X => this.backingVector.X; |
|||
|
|||
/// <summary>
|
|||
/// Gets the Cb chroma component.
|
|||
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float Y => this.backingVector.Y; |
|||
|
|||
/// <summary>
|
|||
/// Gets the Cr chroma component.
|
|||
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float Z => this.backingVector.Z; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this <see cref="CieXyz"/> is empty.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IsEmpty => this.Equals(Empty); |
|||
|
|||
/// <inheritdoc />
|
|||
public Vector3 Vector => this.backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="CieXyz"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="CieXyz"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="CieXyz"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator ==(CieXyz left, CieXyz right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="CieXyz"/> objects for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="CieXyz"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="CieXyz"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator !=(CieXyz left, CieXyz right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return this.backingVector.GetHashCode(); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return "CieXyz [ Empty ]"; |
|||
} |
|||
|
|||
return $"CieXyz [ X={this.X:#0.##}, Y={this.Y:#0.##}, Z={this.Z:#0.##} ]"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is CieXyz) |
|||
{ |
|||
return this.Equals((CieXyz)obj); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(CieXyz other) |
|||
{ |
|||
return this.backingVector.Equals(other.backingVector); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool AlmostEquals(CieXyz other, float precision) |
|||
{ |
|||
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector); |
|||
|
|||
return result.X <= precision |
|||
&& result.Y <= precision |
|||
&& result.Z <= precision; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// <copyright file="CieConstants.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
/// <summary>
|
|||
/// Constants use for Cie conversion calculations
|
|||
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html"/>
|
|||
/// </summary>
|
|||
internal static class CieConstants |
|||
{ |
|||
/// <summary>
|
|||
/// 216F / 24389F
|
|||
/// </summary>
|
|||
public const float Epsilon = 0.008856452F; |
|||
|
|||
/// <summary>
|
|||
/// 24389F / 27F
|
|||
/// </summary>
|
|||
public const float Kappa = 903.2963F; |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// <copyright file="ColorConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using System; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
|
|||
/// </summary>
|
|||
public partial class ColorConverter |
|||
{ |
|||
/// <summary>
|
|||
/// Performs chromatic adaptation of given XYZ color.
|
|||
/// Target white point is <see cref="WhitePoint"/>.
|
|||
/// </summary>
|
|||
public CieXyz Adapt(CieXyz color, CieXyz sourceWhitePoint) |
|||
{ |
|||
Guard.NotNull(color, nameof(color)); |
|||
Guard.NotNull(sourceWhitePoint, nameof(sourceWhitePoint)); |
|||
|
|||
if (!this.IsChromaticAdaptationPerformed) |
|||
{ |
|||
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); |
|||
} |
|||
|
|||
return this.ChromaticAdaptation.Transform(color, sourceWhitePoint, this.WhitePoint); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
// <copyright file="ColorConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using Implementation; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
|
|||
/// </summary>
|
|||
public partial class ColorConverter |
|||
{ |
|||
/// <summary>
|
|||
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLab"/>
|
|||
/// </summary>
|
|||
/// <param name="color">The color to convert.</param>
|
|||
/// <returns>The <see cref="CieLab"/></returns>
|
|||
public CieLab ToCieLab(CieXyz color) |
|||
{ |
|||
Guard.NotNull(color, nameof(color)); |
|||
|
|||
// Adaptation
|
|||
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed |
|||
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint) |
|||
: color; |
|||
|
|||
// Conversion
|
|||
CieXyzToCieLabConverter converter = new CieXyzToCieLabConverter(this.TargetLabWhitePoint); |
|||
return converter.Convert(adapted); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
// <copyright file="ColorConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using Implementation; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
|
|||
/// </summary>
|
|||
public partial class ColorConverter |
|||
{ |
|||
private static readonly CieLabToCieXyzConverter CieLabToCieXyzConverter = new CieLabToCieXyzConverter(); |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="CieLab"/> into a <see cref="CieXyz"/>
|
|||
/// </summary>
|
|||
/// <param name="color">The color to convert.</param>
|
|||
/// <returns>The <see cref="CieXyz"/></returns>
|
|||
public CieXyz ToCieXyz(CieLab color) |
|||
{ |
|||
Guard.NotNull(color, nameof(color)); |
|||
|
|||
// Conversion
|
|||
|
|||
CieXyz unadapted = CieLabToCieXyzConverter.Convert(color); |
|||
|
|||
// Adaptation
|
|||
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed |
|||
? unadapted |
|||
: this.Adapt(unadapted, color.WhitePoint); |
|||
|
|||
return adapted; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="Lms"/> into a <see cref="CieXyz"/>
|
|||
/// </summary>
|
|||
/// <param name="color">The color to convert.</param>
|
|||
/// <returns>The <see cref="CieXyz"/></returns>
|
|||
public CieXyz ToCieXyz(Lms color) |
|||
{ |
|||
Guard.NotNull(color, nameof(color)); |
|||
|
|||
// Conversion
|
|||
return this.cachedCieXyzAndLmsConverter.Convert(color); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
// <copyright file="ColorConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using Implementation; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
|
|||
/// </summary>
|
|||
public partial class ColorConverter |
|||
{ |
|||
/// <summary>
|
|||
/// Converts a <see cref="CieXyz"/> into a <see cref="Lms"/>
|
|||
/// </summary>
|
|||
/// <param name="color">The color to convert.</param>
|
|||
/// <returns>The <see cref="Lms"/></returns>
|
|||
public Lms ToLms(CieXyz color) |
|||
{ |
|||
Guard.NotNull(color, nameof(color)); |
|||
|
|||
// Conversion
|
|||
return this.cachedCieXyzAndLmsConverter.Convert(color); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
// <copyright file="ColorConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using System.Numerics; |
|||
using Implementation; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
|
|||
/// </summary>
|
|||
public partial class ColorConverter |
|||
{ |
|||
private Matrix4x4 transformationMatrix; |
|||
private CieXyzAndLmsConverter cachedCieXyzAndLmsConverter; |
|||
|
|||
/// <summary>
|
|||
/// The default whitepoint used for converting to CieLab
|
|||
/// </summary>
|
|||
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ColorConverter"/> class.
|
|||
/// </summary>
|
|||
public ColorConverter() |
|||
{ |
|||
// Note the order here this is important.
|
|||
this.WhitePoint = DefaultWhitePoint; |
|||
this.LmsAdaptationMatrix = CieXyzAndLmsConverter.DefaultTransformationMatrix; |
|||
this.ChromaticAdaptation = new VonKriesChromaticAdaptation(this.cachedCieXyzAndLmsConverter, this.cachedCieXyzAndLmsConverter); |
|||
this.TargetLabWhitePoint = CieLab.DefaultWhitePoint; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space.
|
|||
/// When null, no adaptation will be performed.
|
|||
/// </summary>
|
|||
public CieXyz WhitePoint { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the white point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information)
|
|||
/// Defaults to: <see cref="CieLab.DefaultWhitePoint"/>.
|
|||
/// </summary>
|
|||
public CieXyz TargetLabWhitePoint { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the chromatic adaptation method used. When null, no adaptation will be performed.
|
|||
/// </summary>
|
|||
public IChromaticAdaptation ChromaticAdaptation { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets transformation matrix used in conversion to <see cref="Lms"/>,
|
|||
/// also used in the default Von Kries Chromatic Adaptation method.
|
|||
/// </summary>
|
|||
public Matrix4x4 LmsAdaptationMatrix |
|||
{ |
|||
get { return this.transformationMatrix; } |
|||
set |
|||
{ |
|||
this.transformationMatrix = value; |
|||
|
|||
if (this.cachedCieXyzAndLmsConverter == null) |
|||
{ |
|||
this.cachedCieXyzAndLmsConverter = new CieXyzAndLmsConverter(value); |
|||
} |
|||
else |
|||
{ |
|||
this.cachedCieXyzAndLmsConverter.TransformationMatrix = value; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether chromatic adaptation has been performed.
|
|||
/// </summary>
|
|||
private bool IsChromaticAdaptationPerformed => this.ChromaticAdaptation != null; |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
// <copyright file="IChromaticAdaptation.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Chromatic adaptation.
|
|||
/// A linear transformation of a source color (XS, YS, ZS) into a destination color (XD, YD, ZD) by a linear transformation [M]
|
|||
/// which is dependent on the source reference white (XWS, YWS, ZWS) and the destination reference white (XWD, YWD, ZWD).
|
|||
/// </summary>
|
|||
public interface IChromaticAdaptation |
|||
{ |
|||
/// <summary>
|
|||
/// Performs a linear transformation of a source color in to the destination color.
|
|||
/// </summary>
|
|||
/// <remarks>Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates).</remarks>
|
|||
/// <param name="sourceColor">The source color.</param>
|
|||
/// <param name="sourceWhitePoint">The source white point.</param>
|
|||
/// <param name="targetWhitePoint">The target white point.</param>
|
|||
/// <returns>The <see cref="CieXyz"/></returns>
|
|||
CieXyz Transform(CieXyz sourceColor, CieXyz sourceWhitePoint, CieXyz targetWhitePoint); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// <copyright file="IColorConversion.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
/// <summary>
|
|||
/// Converts color between two color spaces.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The input color type.</typeparam>
|
|||
/// <typeparam name="TResult">The result color type.</typeparam>
|
|||
public interface IColorConversion<in T, out TResult> |
|||
{ |
|||
/// <summary>
|
|||
/// Performs the conversion from the <see cref="T"/> input to an instance of the output <see cref="TResult"/> type.
|
|||
/// </summary>
|
|||
/// <param name="input">The input color instance.</param>
|
|||
/// <returns>The <see cref="TResult"/></returns>
|
|||
TResult Convert(T input); |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
// <copyright file="CieLabToCieXyzConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion.Implementation |
|||
{ |
|||
using System; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts from <see cref="CieLab"/> to <see cref="CieXyz"/>.
|
|||
/// </summary>
|
|||
public class CieLabToCieXyzConverter : IColorConversion<CieLab, CieXyz> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public CieXyz Convert(CieLab input) |
|||
{ |
|||
Guard.NotNull(input, nameof(input)); |
|||
|
|||
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
|
|||
float l = input.L, a = input.A, b = input.B; |
|||
float fy = (l + 16) / 116F; |
|||
float fx = a / 500F + fy; |
|||
float fz = fy - b / 200F; |
|||
|
|||
float fx3 = (float)Math.Pow(fx, 3D); |
|||
float fz3 = (float)Math.Pow(fz, 3D); |
|||
|
|||
float xr = fx3 > CieConstants.Epsilon ? fx3 : (116F * fx - 16F) / CieConstants.Kappa; |
|||
float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? (float)Math.Pow((l + 16F) / 116F, 3D) : l / CieConstants.Kappa; |
|||
float zr = fz3 > CieConstants.Epsilon ? fz3 : (116F * fz - 16F) / CieConstants.Kappa; |
|||
|
|||
float wx = input.WhitePoint.X, wy = input.WhitePoint.Y, wz = input.WhitePoint.Z; |
|||
|
|||
// Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white)
|
|||
xr = xr.Clamp(0, 1F); |
|||
yr = yr.Clamp(0, 1F); |
|||
zr = zr.Clamp(0, 1F); |
|||
|
|||
float x = xr * wx; |
|||
float y = yr * wy; |
|||
float z = zr * wz; |
|||
|
|||
return new CieXyz(x, y, z); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
// <copyright file="CieXyzToCieLabConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion.Implementation |
|||
{ |
|||
using System; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Converts from <see cref="CieXyz"/> to <see cref="CieLab"/>.
|
|||
/// </summary>
|
|||
public class CieXyzToCieLabConverter : IColorConversion<CieXyz, CieLab> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class.
|
|||
/// </summary>
|
|||
public CieXyzToCieLabConverter() |
|||
: this(CieLab.DefaultWhitePoint) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class.
|
|||
/// </summary>
|
|||
/// <param name="labWhitePoint">The target reference lab white point</param>
|
|||
public CieXyzToCieLabConverter(CieXyz labWhitePoint) |
|||
{ |
|||
this.LabWhitePoint = labWhitePoint; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the target reference whitepoint. When not set, <see cref="CieLab.DefaultWhitePoint"/> is used.
|
|||
/// </summary>
|
|||
public CieXyz LabWhitePoint { get; } |
|||
|
|||
/// <inheritdoc />
|
|||
public CieLab Convert(CieXyz input) |
|||
{ |
|||
Guard.NotNull(input, nameof(input)); |
|||
|
|||
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
|
|||
float wx = this.LabWhitePoint.X, wy = this.LabWhitePoint.Y, wz = this.LabWhitePoint.Z; |
|||
|
|||
float xr = input.X / wx, yr = input.Y / wy, zr = input.Z / wz; |
|||
|
|||
float fx = xr > CieConstants.Epsilon ? (float)Math.Pow(xr, 0.333333333333333D) : (CieConstants.Kappa * xr + 16F) / 116F; |
|||
float fy = yr > CieConstants.Epsilon ? (float)Math.Pow(yr, 0.333333333333333D) : (CieConstants.Kappa * yr + 16F) / 116F; |
|||
float fz = zr > CieConstants.Epsilon ? (float)Math.Pow(zr, 0.333333333333333D) : (CieConstants.Kappa * zr + 16F) / 116F; |
|||
|
|||
float l = (116F * fy) - 16F; |
|||
float a = 500F * (fx - fy); |
|||
float b = 200F * (fy - fz); |
|||
|
|||
return new CieLab(l, a, b, this.LabWhitePoint); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
// <copyright file="CieXyzAndLmsConverter.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
using System.Numerics; |
|||
|
|||
namespace ImageSharp.Colors.Conversion.Implementation |
|||
{ |
|||
using Spaces; |
|||
|
|||
public class CieXyzAndLmsConverter : IColorConversion<CieXyz, Lms>, IColorConversion<Lms, CieXyz> |
|||
{ |
|||
/// <summary>
|
|||
/// Default transformation matrix used, when no other is set. (Bradford)
|
|||
/// <see cref="LmsAdaptationMatrix"/>
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 DefaultTransformationMatrix = LmsAdaptationMatrix.Bradford; |
|||
|
|||
private Matrix4x4 inverseTransformationMatrix; |
|||
private Matrix4x4 transformationMatrix; |
|||
|
|||
/// <summary>
|
|||
/// Transformation matrix used for the conversion (definition of the cone response domain).
|
|||
/// <see cref="LmsAdaptationMatrix"/>
|
|||
/// </summary>
|
|||
public Matrix4x4 TransformationMatrix |
|||
{ |
|||
get { return this.transformationMatrix; } |
|||
internal set |
|||
{ |
|||
this.transformationMatrix = value; |
|||
Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieXyzAndLmsConverter"/> class.
|
|||
/// </summary>
|
|||
public CieXyzAndLmsConverter() |
|||
: this(DefaultTransformationMatrix) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CieXyzAndLmsConverter"/> class.
|
|||
/// </summary>
|
|||
/// <param name="transformationMatrix">
|
|||
/// Definition of the cone response domain (see <see cref="LmsAdaptationMatrix"/>),
|
|||
/// if not set <see cref="DefaultTransformationMatrix"/> will be used.
|
|||
/// </param>
|
|||
public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix) |
|||
{ |
|||
this.TransformationMatrix = transformationMatrix; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public Lms Convert(CieXyz input) |
|||
{ |
|||
Guard.NotNull(input, nameof(input)); |
|||
|
|||
Vector3 vector = Vector3.Transform(input.Vector, this.transformationMatrix); |
|||
return new Lms(vector); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public CieXyz Convert(Lms input) |
|||
{ |
|||
Vector3 vector = Vector3.Transform(input.Vector, this.inverseTransformationMatrix); |
|||
return new CieXyz(vector); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
// <copyright file="LmsAdaptationMatrix.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
namespace ImageSharp.Colors.Conversion.Implementation |
|||
{ |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// AdaptionMatrix3X3 used for transformation from XYZ to LMS, defining the cone response domain.
|
|||
/// Used in <see cref="IChromaticAdaptation"/>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// AdaptionMatrix3X3 data obtained from:
|
|||
/// Two New von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization
|
|||
/// S. Bianco, R. Schettini
|
|||
/// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy
|
|||
/// http://www.ivl.disco.unimib.it/papers2003/CRA-CAT.pdf
|
|||
/// </remarks>
|
|||
public static class LmsAdaptationMatrix |
|||
{ |
|||
/// <summary>
|
|||
/// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65)
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 VonKriesHPEAdjusted |
|||
= new Matrix4x4() |
|||
{ |
|||
M11 = 0.40024F, M12 = 0.7076F, M13 = -0.08081F, |
|||
M21 = -0.2263F, M22 = 1.16532F, M23 = 0.0457F, |
|||
M31 = 0, M32 = 0, M33 = 0.91822F, |
|||
M44 = 1F // Important for inverse transforms.
|
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez for equal energy)
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 VonKriesHPE |
|||
= new Matrix4x4 |
|||
{ |
|||
M11 = 0.3897F, M12 = 0.6890F, M13 = -0.0787F, |
|||
M21 = -0.2298F, M22 = 1.1834F, M23 = 0.0464F, |
|||
M31 = 0, M32 = 0, M33 = 1F, |
|||
M44 = 1F |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// XYZ scaling chromatic adaptation transform matrix
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 XYZScaling = Matrix4x4.Identity; |
|||
|
|||
/// <summary>
|
|||
/// Bradford chromatic adaptation transform matrix (used in CMCCAT97)
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 Bradford |
|||
= new Matrix4x4 |
|||
{ |
|||
M11 = 0.8951F, M12 = 0.2664F, M13 = -0.1614F, |
|||
M21 = -0.7502F, M22 = 1.7135F, M23 = 0.0367F, |
|||
M31 = 0.0389F, M32 = -0.0685F, M33 = 1.0296F, |
|||
M44 = 1F |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Spectral sharpening and the Bradford transform
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 BradfordSharp |
|||
= new Matrix4x4 |
|||
{ |
|||
M11 = 1.2694F, M12 = -0.0988F, M13 = -0.1706F, |
|||
M21 = -0.8364F, M22 = 1.8006F, M23 = 0.0357F, |
|||
M31 = 0.0297F, M32 = -0.0315F, M33 = 1.0018F, |
|||
M44 = 1F |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// CMCCAT2000 (fitted from all available color data sets)
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 CMCCAT2000 |
|||
= new Matrix4x4 |
|||
{ |
|||
M11 = 0.7982F, M12 = 0.3389F, M13 = -0.1371F, |
|||
M21 = -0.5918F, M22 = 1.5512F, M23 = 0.0406F, |
|||
M31 = 0.0008F, M32 = 0.239F, M33 = 0.9753F, |
|||
M44 = 1F |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// CAT02 (optimized for minimizing CIELAB differences)
|
|||
/// </summary>
|
|||
public static readonly Matrix4x4 CAT02 |
|||
= new Matrix4x4 |
|||
{ |
|||
M11 = 0.7328F, M12 = 0.4296F, M13 = -0.1624F, |
|||
M21 = -0.7036F, M22 = 1.6975F, M23 = 0.0061F, |
|||
M31 = 0.0030F, M32 = 0.0136F, M33 = 0.9834F, |
|||
M44 = 1F |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
// <copyright file="VonKriesChromaticAdaptation.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Conversion |
|||
{ |
|||
using System.Numerics; |
|||
|
|||
using Implementation; |
|||
using Spaces; |
|||
|
|||
/// <summary>
|
|||
/// Basic implementation of the von Kries chromatic adaptation model
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Transformation described here:
|
|||
/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
|
|||
/// </remarks>
|
|||
public class VonKriesChromaticAdaptation : IChromaticAdaptation |
|||
{ |
|||
private readonly IColorConversion<CieXyz, Lms> conversionToLms; |
|||
|
|||
private readonly IColorConversion<Lms, CieXyz> conversionToXyz; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
|
|||
/// </summary>
|
|||
public VonKriesChromaticAdaptation() |
|||
: this(new CieXyzAndLmsConverter()) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
|
|||
/// </summary>
|
|||
/// <param name="transformationMatrix">
|
|||
/// The transformation matrix used for the conversion (definition of the cone response domain).
|
|||
/// <see cref="LmsAdaptationMatrix"/>
|
|||
/// </param>
|
|||
public VonKriesChromaticAdaptation(Matrix4x4 transformationMatrix) |
|||
: this(new CieXyzAndLmsConverter(transformationMatrix)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
|
|||
/// </summary>
|
|||
/// <param name="converter"></param>
|
|||
private VonKriesChromaticAdaptation(CieXyzAndLmsConverter converter) |
|||
: this(converter, converter) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
|
|||
/// </summary>
|
|||
/// <param name="conversionToLms">The <see cref="Lms"/> color converter.</param>
|
|||
/// <param name="conversionToCieXyz">The <see cref="CieXyz"/> color converter.</param>
|
|||
public VonKriesChromaticAdaptation(IColorConversion<CieXyz, Lms> conversionToLms, IColorConversion<Lms, CieXyz> conversionToCieXyz) |
|||
{ |
|||
Guard.NotNull(conversionToLms, nameof(conversionToLms)); |
|||
Guard.NotNull(conversionToCieXyz, nameof(conversionToCieXyz)); |
|||
|
|||
this.conversionToLms = conversionToLms; |
|||
this.conversionToXyz = conversionToCieXyz; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public CieXyz Transform(CieXyz sourceColor, CieXyz sourceWhitePoint, CieXyz targetWhitePoint) |
|||
{ |
|||
Guard.NotNull(sourceColor, nameof(sourceColor)); |
|||
Guard.NotNull(sourceWhitePoint, nameof(sourceWhitePoint)); |
|||
Guard.NotNull(targetWhitePoint, nameof(targetWhitePoint)); |
|||
|
|||
if (sourceWhitePoint.Equals(targetWhitePoint)) |
|||
{ |
|||
return sourceColor; |
|||
} |
|||
|
|||
Lms sourceColorLms = this.conversionToLms.Convert(sourceColor); |
|||
Lms sourceWhitePointLms = this.conversionToLms.Convert(sourceWhitePoint); |
|||
Lms targetWhitePointLms = this.conversionToLms.Convert(targetWhitePoint); |
|||
|
|||
Vector3 vector = new Vector3(targetWhitePointLms.L / sourceWhitePointLms.L, targetWhitePointLms.M / sourceWhitePointLms.M, targetWhitePointLms.S / sourceWhitePointLms.S); |
|||
|
|||
Lms targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.Vector)); |
|||
return this.conversionToXyz.Convert(targetColorLms); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
// <copyright file="IAlmostEquatable.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
using System; |
|||
|
|||
/// <summary>
|
|||
/// Defines a generalized method that a value type or class implements to create
|
|||
/// a type-specific method for determining approximate equality of instances.
|
|||
/// </summary>
|
|||
/// <typeparam name="TColor">The type of objects to compare.</typeparam>
|
|||
/// <typeparam name="TPrecision">The object specifying the type to specify precision with.</typeparam>
|
|||
public interface IAlmostEquatable<in TColor, in TPrecision> |
|||
where TPrecision : struct, IComparable<TPrecision> |
|||
{ |
|||
/// <summary>
|
|||
/// Indicates whether the current object is equal to another object of the same type
|
|||
/// when compared to the specified precision level.
|
|||
/// </summary>
|
|||
/// <param name="other">An object to compare with this object.</param>
|
|||
/// <param name="precision">The object specifying the level of precision.</param>
|
|||
/// <returns>
|
|||
/// true if the current object is equal to the other parameter; otherwise, false.
|
|||
/// </returns>
|
|||
bool AlmostEquals(TColor other, TPrecision precision); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
// <copyright file="IColorVector.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Color represented as a vector in its color space
|
|||
/// </summary>
|
|||
public interface IColorVector |
|||
{ |
|||
/// <summary>
|
|||
/// The vector representation of the color
|
|||
/// </summary>
|
|||
Vector3 Vector { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// <copyright file="ICompanding.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
/// <summary>
|
|||
/// Pair of companding functions for <see cref="IRgbWorkingSpace"/>.
|
|||
/// Used for conversion to <see cref="CieXyz"/> and backwards.
|
|||
/// See also: <seealso cref="IRgbWorkingSpace.Companding"/>
|
|||
/// </summary>
|
|||
public interface ICompanding |
|||
{ |
|||
/// <summary>
|
|||
/// Companded channel is made linear with respect to the energy.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// For more info see:
|
|||
/// http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
|
|||
/// </remarks>
|
|||
float InverseCompanding(float channel); |
|||
|
|||
/// <summary>
|
|||
/// Uncompanded channel (linear) is made nonlinear (depends on the RGB color system).
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// For more info see:
|
|||
/// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html
|
|||
/// </remarks>
|
|||
float Companding(float channel); |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// <copyright file="IRgbWorkingSpace.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
/// <summary>
|
|||
/// Encasulates the RGB working color space
|
|||
/// </summary>
|
|||
public interface IRgbWorkingSpace |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the reference white of the color space
|
|||
/// </summary>
|
|||
CieXyz WhitePoint { get; } |
|||
|
|||
/// <summary>
|
|||
/// Chromaticity coordinates of the primaries
|
|||
/// </summary>
|
|||
// RGBPrimariesChromaticityCoordinates ChromaticityCoordinates { get; }
|
|||
|
|||
/// <summary>
|
|||
/// The companding function associated with the RGB color system.
|
|||
/// Used for conversion to XYZ and backwards.
|
|||
/// See this for more information:
|
|||
/// http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
|
|||
/// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html
|
|||
/// </summary>
|
|||
ICompanding Companding { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
/// <summary>
|
|||
/// The well known standard illuminants.
|
|||
/// Standard illuminants provide a basis for comparing images or colors recorded under different lighting
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Coefficients taken from:
|
|||
/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
|
|||
/// <br />
|
|||
/// Descriptions taken from:
|
|||
/// http://en.wikipedia.org/wiki/Standard_illuminant
|
|||
/// </remarks>
|
|||
public static class Illuminants |
|||
{ |
|||
/// <summary>
|
|||
/// Incandescent / Tungsten
|
|||
/// </summary>
|
|||
public static readonly CieXyz A = new CieXyz(1.09850F, 1F, 0.35585F); |
|||
|
|||
/// <summary>
|
|||
/// Direct sunlight at noon (obsoleteF)
|
|||
/// </summary>
|
|||
public static readonly CieXyz B = new CieXyz(0.99072F, 1F, 0.85223F); |
|||
|
|||
/// <summary>
|
|||
/// Average / North sky Daylight (obsoleteF)
|
|||
/// </summary>
|
|||
public static readonly CieXyz C = new CieXyz(0.98074F, 1F, 1.18232F); |
|||
|
|||
/// <summary>
|
|||
/// Horizon Light. ICC profile PCS
|
|||
/// </summary>
|
|||
public static readonly CieXyz D50 = new CieXyz(0.96422F, 1F, 0.82521F); |
|||
|
|||
/// <summary>
|
|||
/// Mid-morning / Mid-afternoon Daylight
|
|||
/// </summary>
|
|||
public static readonly CieXyz D55 = new CieXyz(0.95682F, 1F, 0.92149F); |
|||
|
|||
/// <summary>
|
|||
/// Noon Daylight: TelevisionF, sRGB color space
|
|||
/// </summary>
|
|||
public static readonly CieXyz D65 = new CieXyz(0.95047F, 1F, 1.08883F); |
|||
|
|||
/// <summary>
|
|||
/// North sky Daylight
|
|||
/// </summary>
|
|||
public static readonly CieXyz D75 = new CieXyz(0.94972F, 1F, 1.22638F); |
|||
|
|||
/// <summary>
|
|||
/// Equal energy
|
|||
/// </summary>
|
|||
public static readonly CieXyz E = new CieXyz(1F, 1F, 1F); |
|||
|
|||
/// <summary>
|
|||
/// Cool White Fluorescent
|
|||
/// </summary>
|
|||
public static readonly CieXyz F2 = new CieXyz(0.99186F, 1F, 0.67393F); |
|||
|
|||
/// <summary>
|
|||
/// D65 simulatorF, Daylight simulator
|
|||
/// </summary>
|
|||
public static readonly CieXyz F7 = new CieXyz(0.95041F, 1F, 1.08747F); |
|||
|
|||
/// <summary>
|
|||
/// Philips TL84F, Ultralume 40
|
|||
/// </summary>
|
|||
public static readonly CieXyz F11 = new CieXyz(1.00962F, 1F, 0.64350F); |
|||
} |
|||
} |
|||
@ -0,0 +1,156 @@ |
|||
// <copyright file="Lms.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Colors.Spaces |
|||
{ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// LMS is a color space represented by the response of the three types of cones of the human eye,
|
|||
/// named after their responsivity (sensitivity) at long, medium and short wavelengths.
|
|||
/// <see href="https://en.wikipedia.org/wiki/LMS_color_space"/>
|
|||
/// </summary>
|
|||
public struct Lms : IColorVector, IEquatable<Lms>, IAlmostEquatable<Lms, float> |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="Lms"/> that has Y, Cb, and Cr values set to zero.
|
|||
/// </summary>
|
|||
public static readonly Lms Empty = default(Lms); |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private readonly Vector3 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Lms"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="l">L represents the responsivity at long wavelengths.</param>
|
|||
/// <param name="m">M represents the responsivity at medium wavelengths.</param>
|
|||
/// <param name="s">S represents the responsivity at short wavelengths.</param>
|
|||
public Lms(float l, float m, float s) |
|||
: this(new Vector3(l, m, s)) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Lms"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector representing the x, y, z components.</param>
|
|||
public Lms(Vector3 vector) |
|||
: this() |
|||
{ |
|||
// Not clamping as documentation about this space seems to indicate "usual" ranges
|
|||
this.backingVector = vector; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the L long component.
|
|||
/// <remarks>A value usually ranging between -1 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float L => this.backingVector.X; |
|||
|
|||
/// <summary>
|
|||
/// Gets the M medium component.
|
|||
/// <remarks>A value usually ranging between -1 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float M => this.backingVector.Y; |
|||
|
|||
/// <summary>
|
|||
/// Gets the S short component.
|
|||
/// <remarks>A value usually ranging between -1 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float S => this.backingVector.Z; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this <see cref="Lms"/> is empty.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IsEmpty => this.Equals(Empty); |
|||
|
|||
/// <inheritdoc />
|
|||
public Vector3 Vector => this.backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Lms"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Lms"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Lms"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator ==(Lms left, Lms right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Lms"/> objects for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Lms"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Lms"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator !=(Lms left, Lms right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return this.backingVector.GetHashCode(); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return "Lms [ Empty ]"; |
|||
} |
|||
|
|||
return $"Lms [ L={this.L:#0.##}, M={this.M:#0.##}, S={this.S:#0.##} ]"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is Lms) |
|||
{ |
|||
return this.Equals((Lms)obj); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(Lms other) |
|||
{ |
|||
return this.backingVector.Equals(other.backingVector); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool AlmostEquals(Lms other, float precision) |
|||
{ |
|||
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector); |
|||
|
|||
return result.X <= precision |
|||
&& result.Y <= precision |
|||
&& result.Z <= precision; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
namespace ImageSharp.Tests |
|||
{ |
|||
using ImageSharp.Colors.Conversion; |
|||
using System.Collections.Generic; |
|||
using ImageSharp.Colors.Spaces; |
|||
using Xunit; |
|||
|
|||
/// <summary>
|
|||
/// Tests <see cref="CieXyz"/>-<see cref="CieLab"/> conversions.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Test data generated using:
|
|||
/// http://www.brucelindbloom.com/index.html?ColorCalculator.html
|
|||
/// </remarks>
|
|||
public class CieXyzAndCieLabConversionTest |
|||
{ |
|||
private static readonly IEqualityComparer<float> FloatComparerLabPrecision = new ApproximateFloatComparer(4); |
|||
private static readonly IEqualityComparer<float> FloatComparerXyzPrecision = new ApproximateFloatComparer(6); |
|||
|
|||
/// <summary>
|
|||
/// Tests conversion from <see cref="CieLab"/> to <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>).
|
|||
/// </summary>
|
|||
[Theory] |
|||
[InlineData(100, 0, 0, 0.95047, 1, 1.08883)] |
|||
[InlineData(0, 0, 0, 0, 0, 0)] |
|||
[InlineData(0, 431.0345, 0, 0.95047, 0, 0)] |
|||
[InlineData(100, -431.0345, 172.4138, 0, 1, 0)] |
|||
[InlineData(0, 0, -172.4138, 0, 0, 1.08883)] |
|||
[InlineData(45.6398, 39.8753, 35.2091, 0.216938, 0.150041, 0.048850)] |
|||
[InlineData(77.1234, -40.1235, 78.1120, 0.358530, 0.517372, 0.076273)] |
|||
[InlineData(10, -400, 20, 0, 0.011260, 0)] |
|||
public void Convert_Lab_to_XYZ(float l, float a, float b, float x, float y, float z) |
|||
{ |
|||
// Arrange
|
|||
CieLab input = new CieLab(l, a, b, Illuminants.D65); |
|||
ColorConverter converter = new ColorConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; |
|||
|
|||
// Act
|
|||
CieXyz output = converter.ToCieXyz(input); |
|||
|
|||
// Assert
|
|||
Assert.Equal(output.X, x, FloatComparerXyzPrecision); |
|||
Assert.Equal(output.Y, y, FloatComparerXyzPrecision); |
|||
Assert.Equal(output.Z, z, FloatComparerXyzPrecision); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests conversion from <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>) to <see cref="CieLab"/>.
|
|||
/// </summary>
|
|||
[Theory] |
|||
[InlineData(0.95047, 1, 1.08883, 100, 0, 0)] |
|||
[InlineData(0, 0, 0, 0, 0, 0)] |
|||
[InlineData(0.95047, 0, 0, 0, 431.0345, 0)] |
|||
[InlineData(0, 1, 0, 100, -431.0345, 172.4138)] |
|||
[InlineData(0, 0, 1.08883, 0, 0, -172.4138)] |
|||
[InlineData(0.216938, 0.150041, 0.048850, 45.6398, 39.8753, 35.2091)] |
|||
public void Convert_XYZ_to_Lab(float x, float y, float z, float l, float a, float b) |
|||
{ |
|||
// Arrange
|
|||
CieXyz input = new CieXyz(x, y, z); |
|||
ColorConverter converter = new ColorConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; |
|||
|
|||
// Act
|
|||
CieLab output = converter.ToCieLab(input); |
|||
|
|||
// Assert
|
|||
Assert.Equal(output.L, l, FloatComparerLabPrecision); |
|||
Assert.Equal(output.A, a, FloatComparerLabPrecision); |
|||
Assert.Equal(output.B, b, FloatComparerLabPrecision); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
namespace ImageSharp.Tests |
|||
{ |
|||
using ImageSharp.Colors.Conversion; |
|||
using System.Collections.Generic; |
|||
using ImageSharp.Colors.Spaces; |
|||
using Xunit; |
|||
|
|||
/// <summary>
|
|||
/// Tests <see cref="CieXyz"/>-<see cref="CieLab"/> conversions.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Test data generated using original colorful library.
|
|||
/// </remarks>
|
|||
public class CieXyzAndLmsConversionTest |
|||
{ |
|||
private static readonly IEqualityComparer<float> FloatComparer = new ApproximateFloatComparer(6); |
|||
|
|||
/// <summary>
|
|||
/// Tests conversion from <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>) to <see cref="Lms"/>.
|
|||
/// </summary>
|
|||
[Theory] |
|||
[InlineData(0.941428535, 1.040417467, 1.089532651, 0.95047, 1, 1.08883)] |
|||
[InlineData(0, 0, 0, 0, 0, 0)] |
|||
[InlineData(0.850765697, -0.713042594, 0.036973283, 0.95047, 0, 0)] |
|||
[InlineData(0.2664, 1.7135, -0.0685, 0, 1, 0)] |
|||
[InlineData(-0.175737162, 0.039960061, 1.121059368, 0, 0, 1.08883)] |
|||
[InlineData(0.2262677362, 0.0961411609, 0.0484570397, 0.216938, 0.150041, 0.048850)] |
|||
public void Convert_Lms_to_CieXyz(float l, float m, float s, float x, float y, float z) |
|||
{ |
|||
// Arrange
|
|||
Lms input = new Lms(x, y, z); |
|||
ColorConverter converter = new ColorConverter(); |
|||
|
|||
// Act
|
|||
CieXyz output = converter.ToCieXyz(input); |
|||
|
|||
// Assert
|
|||
Assert.Equal(output.X, l, FloatComparer); |
|||
Assert.Equal(output.Y, m, FloatComparer); |
|||
Assert.Equal(output.Z, s, FloatComparer); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tests conversion from <see cref="CieXyz"/> (<see cref="Illuminants.D65"/>) to <see cref="Lms"/>.
|
|||
/// </summary>
|
|||
[Theory] |
|||
[InlineData(0.95047, 1, 1.08883, 0.941428535, 1.040417467, 1.089532651)] |
|||
[InlineData(0, 0, 0, 0, 0, 0)] |
|||
[InlineData(0.95047, 0, 0, 0.850765697, -0.713042594, 0.036973283)] |
|||
[InlineData(0, 1, 0, 0.2664, 1.7135, -0.0685)] |
|||
[InlineData(0, 0, 1.08883, -0.175737162, 0.039960061, 1.121059368)] |
|||
[InlineData(0.216938, 0.150041, 0.048850, 0.2262677362, 0.0961411609, 0.0484570397)] |
|||
public void Convert_CieXyz_to_Lms(float x, float y, float z, float l, float m, float s) |
|||
{ |
|||
// Arrange
|
|||
CieXyz input = new CieXyz(x, y, z); |
|||
ColorConverter converter = new ColorConverter(); |
|||
|
|||
// Act
|
|||
Lms output = converter.ToLms(input); |
|||
|
|||
// Assert
|
|||
Assert.Equal(output.L, l, FloatComparer); |
|||
Assert.Equal(output.M, m, FloatComparer); |
|||
Assert.Equal(output.S, s, FloatComparer); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using ImageSharp.Colors.Conversion; |
|||
using ImageSharp.Colors.Conversion.Implementation; |
|||
using ImageSharp.Colors.Spaces; |
|||
|
|||
namespace ImageSharp.Tests |
|||
{ |
|||
using System.Collections.Generic; |
|||
using Xunit; |
|||
|
|||
public class ColorConverterAdaptTest |
|||
{ |
|||
private static readonly IEqualityComparer<float> FloatComparer = new ApproximateFloatComparer(4); |
|||
|
|||
[Theory] |
|||
[InlineData(0, 0, 0, 0, 0, 0)] |
|||
[InlineData(0.5, 0.5, 0.5, 0.507233, 0.500000, 0.378943)] |
|||
public void Adapt_CieXyz_D65_To_D50_XyzScaling(float x1, float y1, float z1, float x2, float y2, float z2) |
|||
{ |
|||
// Arrange
|
|||
CieXyz input = new CieXyz(x1, y1, z1); |
|||
CieXyz expectedOutput = new CieXyz(x2, y2, z2); |
|||
ColorConverter converter = new ColorConverter |
|||
{ |
|||
ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XYZScaling), |
|||
WhitePoint = Illuminants.D50 |
|||
}; |
|||
|
|||
// Action
|
|||
CieXyz output = converter.Adapt(input, Illuminants.D65); |
|||
|
|||
// Assert
|
|||
Assert.Equal(output.X, expectedOutput.X, FloatComparer); |
|||
Assert.Equal(output.Y, expectedOutput.Y, FloatComparer); |
|||
Assert.Equal(output.Z, expectedOutput.Z, FloatComparer); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue