diff --git a/src/ImageSharp/Colors/Spaces/CieLab.cs b/src/ImageSharp/Colors/Spaces/CieLab.cs
index 80e8a41a3..984e211a9 100644
--- a/src/ImageSharp/Colors/Spaces/CieLab.cs
+++ b/src/ImageSharp/Colors/Spaces/CieLab.cs
@@ -10,7 +10,7 @@ namespace ImageSharp.Colors.Spaces
using System.Numerics;
///
- /// Represents an CIE LAB 1976 color.
+ /// Represents a CIE L*a*b* 1976 color.
///
///
public struct CieLab : IColorVector, IEquatable, IAlmostEquatable
@@ -68,7 +68,7 @@ namespace ImageSharp.Colors.Spaces
///
/// Initializes a new instance of the struct.
///
- /// The vector representing the l a b components.
+ /// The vector representing the l, a, b components.
/// The reference white point.
public CieLab(Vector3 vector, CieXyz whitePoint)
: this()
diff --git a/src/ImageSharp/Colors/Spaces/CieLch.cs b/src/ImageSharp/Colors/Spaces/CieLch.cs
new file mode 100644
index 000000000..e232a3eb4
--- /dev/null
+++ b/src/ImageSharp/Colors/Spaces/CieLch.cs
@@ -0,0 +1,209 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Colors.Spaces
+{
+ using System;
+ using System.ComponentModel;
+ using System.Numerics;
+
+ ///
+ /// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color.
+ ///
+ ///
+ public struct CieLch : IColorVector, IEquatable, IAlmostEquatable
+ {
+ ///
+ /// D50 standard illuminant.
+ /// Used when reference white is not specified explicitly.
+ ///
+ public static readonly CieXyz DefaultWhitePoint = Illuminants.D50;
+
+ ///
+ /// Represents a that has L, C, H values set to zero.
+ ///
+ public static readonly CieLch Empty = default(CieLch);
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private readonly Vector3 backingVector;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The lightness dimension.
+ /// The chroma, relative saturation.
+ /// The hue in degrees.
+ /// Uses as white point.
+ public CieLch(float l, float c, float h)
+ : this(new Vector3(l, c, h), DefaultWhitePoint)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The lightness dimension.
+ /// The chroma, relative saturation.
+ /// The hue in degrees.
+ /// The reference white point.
+ public CieLch(float l, float c, float h, CieXyz whitePoint)
+ : this(new Vector3(l, c, h), whitePoint)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The vector representing the l, c, h components.
+ /// Uses as white point.
+ public CieLch(Vector3 vector)
+ : this(vector, DefaultWhitePoint)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The vector representing the l, c, h components.
+ /// The reference white point.
+ public CieLch(Vector3 vector, CieXyz whitePoint)
+ : this()
+ {
+ this.backingVector = vector;
+ this.WhitePoint = whitePoint;
+ }
+
+ ///
+ /// Gets the reference white point of this color
+ ///
+ public CieXyz WhitePoint { get; }
+
+ ///
+ /// Gets the lightness dimension.
+ /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).
+ ///
+ public float L => this.backingVector.X;
+
+ ///
+ /// Gets the a chroma component.
+ /// A value ranging from 0 to 100.
+ ///
+ public float C => this.backingVector.Y;
+
+ ///
+ /// Gets the h° hue component in degrees.
+ /// A value ranging from 0 to 360.
+ ///
+ public float H => this.backingVector.Z;
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.Equals(Empty);
+
+ ///
+ public Vector3 Vector => this.backingVector;
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(CieLch left, CieLch right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two objects for inequality
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(CieLch left, CieLch right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return this.backingVector.GetHashCode();
+ }
+
+ ///
+ public override string ToString()
+ {
+ if (this.IsEmpty)
+ {
+ return "CieLch [Empty]";
+ }
+
+ return $"CieLch [ L={this.L:#0.##}, C={this.C:#0.##}, H={this.H:#0.##}]";
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is CieLch)
+ {
+ return this.Equals((CieLch)obj);
+ }
+
+ return false;
+ }
+
+ ///
+ public bool Equals(CieLch other)
+ {
+ return this.backingVector.Equals(other.backingVector);
+ }
+
+ ///
+ public bool AlmostEquals(CieLch other, float precision)
+ {
+ Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
+
+ return result.X <= precision
+ && result.Y <= precision
+ && result.Z <= precision;
+ }
+
+ ///
+ /// Computes the saturation of the color (chroma normalized by lightness)
+ ///
+ ///
+ /// A value ranging from 0 to 100.
+ ///
+ /// The
+ public float Saturation()
+ {
+ float result = 100 * (this.C / this.L);
+
+ if (float.IsNaN(result))
+ {
+ return 0;
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Adapt.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Adapt.cs
index 5123fd0a2..55c54a5a3 100644
--- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Adapt.cs
+++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Adapt.cs
@@ -102,5 +102,51 @@ namespace ImageSharp.Colors.Spaces.Conversion
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
+
+ ///
+ /// Adapts color from the source white point to white point set in .
+ ///
+ /// The color to adapt
+ /// The adapted color
+ public HunterLab Adapt(HunterLab color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ if (!this.IsChromaticAdaptationPerformed)
+ {
+ throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
+ }
+
+ if (color.WhitePoint.Equals(this.TargetHunterLabWhitePoint))
+ {
+ return color;
+ }
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToHunterLab(xyzColor);
+ }
+
+ ///
+ /// Adapts color from the source white point to white point set in .
+ ///
+ /// The color to adapt
+ /// The adapted color
+ public CieLch Adapt(CieLch color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ if (!this.IsChromaticAdaptationPerformed)
+ {
+ throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.");
+ }
+
+ if (color.WhitePoint.Equals(this.TargetLabWhitePoint))
+ {
+ return color;
+ }
+
+ CieLab labColor = this.ToCieLab(color);
+ return this.ToCieLch(labColor);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLab.cs
index 7b0b69a68..418366401 100644
--- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLab.cs
+++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLab.cs
@@ -7,12 +7,18 @@ namespace ImageSharp.Colors.Spaces.Conversion
{
using ImageSharp.Colors.Spaces;
using ImageSharp.Colors.Spaces.Conversion.Implementation.CieLab;
+ using ImageSharp.Colors.Spaces.Conversion.Implementation.CieLch;
///
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
///
public partial class ColorSpaceConverter
{
+ ///
+ /// The converter for converting between CieLch to CieLab.
+ ///
+ private static readonly CieLchToCieLabConverter CieLchToCieLabConverter = new CieLchToCieLabConverter();
+
///
/// Converts a into a
///
@@ -83,5 +89,26 @@ namespace ImageSharp.Colors.Spaces.Conversion
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieLab ToCieLab(CieLch color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ // Conversion (perserving white point)
+ CieLab unadapted = CieLchToCieLabConverter.Convert(color);
+
+ if (!this.IsChromaticAdaptationPerformed)
+ {
+ return unadapted;
+ }
+
+ // Adaptation
+ return this.Adapt(unadapted);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLch.cs
new file mode 100644
index 000000000..0ad1f53d2
--- /dev/null
+++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLch.cs
@@ -0,0 +1,101 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Colors.Spaces.Conversion
+{
+ using ImageSharp.Colors.Spaces.Conversion.Implementation.CieLch;
+
+ ///
+ /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
+ ///
+ public partial class ColorSpaceConverter
+ {
+ ///
+ /// The converter for converting between CieLab to CieLch.
+ ///
+ private static readonly CieLabToCieLchConverter CieLabToCieLchConverter = new CieLabToCieLchConverter();
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieLch ToCieLch(CieXyz color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieLab labColor = this.ToCieLab(color);
+ return this.ToCieLch(labColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieLch ToCieLch(Rgb color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieLab labColor = this.ToCieLab(color);
+ return this.ToCieLch(labColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieLch ToCieLch(LinearRgb color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieLab labColor = this.ToCieLab(color);
+ return this.ToCieLch(labColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieLch ToCieLch(CieLab color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ // Adaptation
+ CieLab adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color;
+
+ // Conversion
+ return CieLabToCieLchConverter.Convert(adapted);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieLch ToCieLch(Lms color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieLab labColor = this.ToCieLab(color);
+ return this.ToCieLch(labColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieLch ToCieLch(HunterLab color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieLab labColor = this.ToCieLab(color);
+ return this.ToCieLch(labColor);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs
index ab42c1043..b5a708dec 100644
--- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs
+++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs
@@ -107,6 +107,22 @@ namespace ImageSharp.Colors.Spaces.Conversion
return adapted;
}
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public CieXyz ToCieXyz(CieLch color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ // Conversion to Lab
+ CieLab labColor = CieLchToCieLabConverter.Convert(color);
+
+ // Conversion to XYZ (incl. adaptation)
+ return this.ToCieXyz(labColor);
+ }
+
///
/// Gets the correct converter for the given rgb working space.
///
diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs
index 630b1cfbe..33fad16c7 100644
--- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs
+++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs
@@ -81,5 +81,18 @@ namespace ImageSharp.Colors.Spaces.Conversion
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public HunterLab ToHunterLab(CieLch color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToHunterLab(xyzColor);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs
index 41cba6f67..1cf577d11 100644
--- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs
+++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs
@@ -53,7 +53,7 @@ namespace ImageSharp.Colors.Spaces.Conversion
///
/// The color to convert.
/// The
- public LinearRgb ToLinearRGB(HunterLab color)
+ public LinearRgb ToLinearRgb(HunterLab color)
{
Guard.NotNull(color, nameof(color));
@@ -66,7 +66,7 @@ namespace ImageSharp.Colors.Spaces.Conversion
///
/// The color to convert.
/// The
- public LinearRgb ToLinearRGB(CieLab color)
+ public LinearRgb ToLinearRgb(CieLab color)
{
Guard.NotNull(color, nameof(color));
@@ -79,7 +79,20 @@ namespace ImageSharp.Colors.Spaces.Conversion
///
/// The color to convert.
/// The
- public LinearRgb ToLinearRGB(Lms color)
+ public LinearRgb ToLinearRgb(Lms color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToLinearRgb(xyzColor);
+ }
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public LinearRgb ToLinearRgb(CieLch color)
{
Guard.NotNull(color, nameof(color));
diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs
index c53e999ec..de9f765ce 100644
--- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs
+++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs
@@ -76,5 +76,18 @@ namespace ImageSharp.Colors.Spaces.Conversion
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLms(xyzColor);
}
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Lms ToLms(CieLch color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToLms(xyzColor);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs
index c82e554e4..879f915dc 100644
--- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs
+++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs
@@ -81,5 +81,18 @@ namespace ImageSharp.Colors.Spaces.Conversion
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
+
+ ///
+ /// Converts a into a
+ ///
+ /// The color to convert.
+ /// The
+ public Rgb ToRgb(CieLch color)
+ {
+ Guard.NotNull(color, nameof(color));
+
+ CieXyz xyzColor = this.ToCieXyz(color);
+ return this.ToRgb(xyzColor);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Spaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs
new file mode 100644
index 000000000..c3721bdf5
--- /dev/null
+++ b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs
@@ -0,0 +1,30 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Colors.Spaces.Conversion.Implementation.CieLch
+{
+ using ImageSharp.Colors.Spaces;
+
+ ///
+ /// Converts from to .
+ ///
+ public class CieLchToCieLabConverter : IColorConversion
+ {
+ ///
+ public CieLab Convert(CieLch input)
+ {
+ DebugGuard.NotNull(input, nameof(input));
+
+ // Conversion algorithm described here: https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
+ float l = input.L, c = input.C, hDegrees = input.H;
+ float hRadians = MathF.DegreeToRadian(hDegrees);
+
+ float a = c * MathF.Cos(hRadians);
+ float b = c * MathF.Sin(hRadians);
+
+ return new CieLab(l, a, b, input.WhitePoint);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Spaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs
new file mode 100644
index 000000000..075aed500
--- /dev/null
+++ b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs
@@ -0,0 +1,38 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Colors.Spaces.Conversion.Implementation.CieLch
+{
+ using ImageSharp.Colors.Spaces;
+
+ ///
+ /// Converts from to .
+ ///
+ internal class CieLabToCieLchConverter : IColorConversion
+ {
+ ///
+ public CieLch Convert(CieLab input)
+ {
+ DebugGuard.NotNull(input, nameof(input));
+
+ // Conversion algorithm described here: https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
+ float l = input.L, a = input.A, b = input.B;
+ float c = MathF.Sqrt((a * a) + (b * b));
+ float hRadians = MathF.Atan2(b, a);
+ float hDegrees = MathF.RadianToDegree(hRadians);
+
+ if (hDegrees > 360)
+ {
+ hDegrees -= 360;
+ }
+ else if (hDegrees < 0)
+ {
+ hDegrees += 360;
+ }
+
+ return new CieLch(l, c, hDegrees, input.WhitePoint);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Spaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs
index 3a0d818d4..988b400e3 100644
--- a/src/ImageSharp/Colors/Spaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs
+++ b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs
@@ -48,7 +48,7 @@ namespace ImageSharp.Colors.Spaces.Conversion.Implementation.Lms
///
/// XYZ scaling chromatic adaptation transform matrix
///
- public static readonly Matrix4x4 XYZScaling = Matrix4x4.Transpose(Matrix4x4.Identity);
+ public static readonly Matrix4x4 XyzScaling = Matrix4x4.Transpose(Matrix4x4.Identity);
///
/// Bradford chromatic adaptation transform matrix (used in CMCCAT97)
diff --git a/src/ImageSharp/Common/Helpers/MathF.cs b/src/ImageSharp/Common/Helpers/MathF.cs
index 2ee700789..7da6b0d77 100644
--- a/src/ImageSharp/Common/Helpers/MathF.cs
+++ b/src/ImageSharp/Common/Helpers/MathF.cs
@@ -19,28 +19,89 @@ namespace ImageSharp
///
public const float PI = (float)Math.PI;
- /// Returns the absolute value of a single-precision floating-point number.
- /// A number that is greater than or equal to , but less than or equal to .
- /// A single-precision floating-point number, x, such that 0 ≤ x ≤.
+ ///
+ /// Returns the absolute value of a single-precision floating-point number.
+ ///
+ ///
+ /// A number that is greater than or equal to , but less than or equal to .
+ ///
+ ///
+ /// A single-precision floating-point number, x, such that 0 ≤ x ≤.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Abs(float f)
{
return Math.Abs(f);
}
- /// Returns the smallest integral value that is greater than or equal to the specified single-precision floating-point number.
- /// A single-precision floating-point number.
- /// The smallest integral value that is greater than or equal to .
+ ///
+ /// Returns the angle whose tangent is the quotient of two specified numbers.
+ ///
+ /// The y coordinate of a point.
+ /// The x coordinate of a point.
+ ///
+ /// An angle, θ, measured in radians, such that -π≤θ≤π, and tan(θ) = y / x, where
+ /// (x, y) is a point in the Cartesian plane. Observe the following: For (x, y) in
+ /// quadrant 1, 0 < θ < π/2.For (x, y) in quadrant 2, π/2 < θ≤π.For (x, y) in quadrant
+ /// 3, -π < θ < -π/2.For (x, y) in quadrant 4, -π/2 < θ < 0.For points on the boundaries
+ /// of the quadrants, the return value is the following:If y is 0 and x is not negative,
+ /// θ = 0.If y is 0 and x is negative, θ = π.If y is positive and x is 0, θ = π/2.If
+ /// y is negative and x is 0, θ = -π/2.If y is 0 and x is 0, θ = 0. If x or y is
+ /// , or if x and y are either or
+ /// , the method returns .
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Atan2(float y, float x)
+ {
+ return (float)Math.Atan2(y, x);
+ }
+
+ ///
+ /// Returns the smallest integral value that is greater than or equal to the specified single-precision floating-point number.
+ ///
+ /// A single-precision floating-point number.
+ ///
+ /// The smallest integral value that is greater than or equal to .
/// If is equal to , ,
/// or , that value is returned.
- /// Note that this method returns a instead of an integral type.
+ /// Note that this method returns a instead of an integral type.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Ceiling(float f)
{
return (float)Math.Ceiling(f);
}
- /// Returns e raised to the specified power.
+ ///
+ /// Returns the cosine of the specified angle.
+ ///
+ /// An angle, measured in radians.
+ ///
+ /// The cosine of . If is equal to , ,
+ /// or , this method returns .
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Cos(float f)
+ {
+ return (float)Math.Cos(f);
+ }
+
+ ///
+ /// Converts a degree (360-periodic) angle to a radian (2*Pi-periodic) angle.
+ ///
+ /// The angle in degrees.
+ ///
+ /// The representing the degree as radians.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float DegreeToRadian(float degree)
+ {
+ return degree * (PI / 180F);
+ }
+
+ ///
+ /// Returns e raised to the specified power.
+ ///
/// A number specifying a power.
///
/// The number e raised to the power .
@@ -53,9 +114,12 @@ namespace ImageSharp
return (float)Math.Exp(f);
}
- /// Returns the largest integer less than or equal to the specified single-precision floating-point number.
+ ///
+ /// Returns the largest integer less than or equal to the specified single-precision floating-point number.
+ ///
/// A single-precision floating-point number.
- /// The largest integer less than or equal to .
+ ///
+ /// The largest integer less than or equal to .
/// If is equal to , ,
/// or , that value is returned.
///
@@ -65,10 +129,13 @@ namespace ImageSharp
return (float)Math.Floor(f);
}
- /// Returns the larger of two single-precision floating-point numbers.
+ ///
+ /// Returns the larger of two single-precision floating-point numbers.
+ ///
/// The first of two single-precision floating-point numbers to compare.
/// The second of two single-precision floating-point numbers to compare.
- /// Parameter or , whichever is larger.
+ ///
+ /// Parameter or , whichever is larger.
/// If , or , or both and are
/// equal to , is returned.
///
@@ -78,19 +145,25 @@ namespace ImageSharp
return Math.Max(val1, val2);
}
- /// Returns the smaller of two single-precision floating-point numbers.
+ ///
+ /// Returns the smaller of two single-precision floating-point numbers.
+ ///
/// The first of two single-precision floating-point numbers to compare.
/// The second of two single-precision floating-point numbers to compare.
- /// Parameter or , whichever is smaller.
+ ///
+ /// Parameter or , whichever is smaller.
/// If , , or both and are equal
- /// to , is returned.
+ /// to , is returned.
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Min(float val1, float val2)
{
return Math.Min(val1, val2);
}
- /// Returns a specified number raised to the specified power.
+ ///
+ /// Returns a specified number raised to the specified power.
+ ///
/// A single-precision floating-point number to be raised to a power.
/// A single-precision floating-point number that specifies a power.
/// The number raised to the power .
@@ -100,7 +173,22 @@ namespace ImageSharp
return (float)Math.Pow(x, y);
}
- /// Rounds a single-precision floating-point value to the nearest integral value.
+ ///
+ /// Converts a radian (2*Pi-periodic) angle to a degree (360-periodic) angle.
+ ///
+ /// The angle in radians.
+ ///
+ /// The representing the degree as radians.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float RadianToDegree(float radian)
+ {
+ return radian / (PI / 180F);
+ }
+
+ ///
+ /// Rounds a single-precision floating-point value to the nearest integral value.
+ ///
/// A single-precision floating-point number to be rounded.
///
/// The integer nearest .
@@ -113,7 +201,9 @@ namespace ImageSharp
return (float)Math.Round(f);
}
- /// Returns the sine of the specified angle.
+ ///
+ /// Returns the sine of the specified angle.
+ ///
/// An angle, measured in radians.
///
/// The sine of .
@@ -126,8 +216,10 @@ namespace ImageSharp
return (float)Math.Sin(f);
}
- /// Returns the square root of a specified number.
- /// The number whose square root is to be found.
+ ///
+ /// Returns the square root of a specified number.
+ ///
+ /// The number whose square root is to be found.
///
/// One of the values in the following table.
/// parameter Return value Zero or positive The positive square root of .
diff --git a/tests/ImageSharp.Tests/Colors/Colorspaces/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colors/Colorspaces/CieLabAndCieLchConversionTests.cs
new file mode 100644
index 000000000..426e21bb4
--- /dev/null
+++ b/tests/ImageSharp.Tests/Colors/Colorspaces/CieLabAndCieLchConversionTests.cs
@@ -0,0 +1,76 @@
+namespace ImageSharp.Tests.Colors.Colorspaces
+{
+ using System.Collections.Generic;
+ using ImageSharp.Colors.Spaces;
+ using ImageSharp.Colors.Spaces.Conversion;
+
+ using Xunit;
+
+ ///
+ /// Tests - conversions.
+ ///
+ ///
+ /// Test data generated using:
+ ///
+ ///
+ public class CieLabAndCieLchConversionTests
+ {
+ private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(4);
+
+ private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter();
+
+ ///
+ /// Tests conversion from to .
+ ///
+ [Theory]
+ [InlineData(0, 0, 0, 0, 0, 0)]
+ [InlineData(54.2917, 106.8391, 40.8526, 54.2917, 80.8125, 69.8851)]
+ [InlineData(100, 0, 0, 100, 0, 0)]
+ [InlineData(100, 50, 180, 100, -50, 0)]
+ [InlineData(10, 36.0555, 56.3099, 10, 20, 30)]
+ [InlineData(10, 36.0555, 56.3099, 10, 20, 30)]
+ [InlineData(10, 36.0555, 123.6901, 10, -20, 30)]
+ [InlineData(10, 36.0555, 303.6901, 10, 20, -30)]
+ [InlineData(10, 36.0555, 236.3099, 10, -20, -30)]
+ public void Convert_Lch_to_Lab(float l, float c, float h, float l2, float a, float b)
+ {
+ // Arrange
+ CieLch input = new CieLch(l, c, h);
+
+ // Act
+ CieLab output = Converter.ToCieLab(input);
+
+ // Assert
+ Assert.Equal(l2, output.L, FloatRoundingComparer);
+ Assert.Equal(a, output.A, FloatRoundingComparer);
+ Assert.Equal(b, output.B, FloatRoundingComparer);
+ }
+
+ ///
+ /// Tests conversion from to .
+ ///
+ [Theory]
+ [InlineData(0, 0, 0, 0, 0, 0)]
+ [InlineData(54.2917, 80.8125, 69.8851, 54.2917, 106.8391, 40.8526)]
+ [InlineData(100, 0, 0, 100, 0, 0)]
+ [InlineData(100, -50, 0, 100, 50, 180)]
+ [InlineData(10, 20, 30, 10, 36.0555, 56.3099)]
+ [InlineData(10, 20, 30, 10, 36.0555, 56.3099)]
+ [InlineData(10, -20, 30, 10, 36.0555, 123.6901)]
+ [InlineData(10, 20, -30, 10, 36.0555, 303.6901)]
+ [InlineData(10, -20, -30, 10, 36.0555, 236.3099)]
+ public void Convert_Lab_to_LCHab(float l, float a, float b, float l2, float c, float h)
+ {
+ // Arrange
+ CieLab input = new CieLab(l, a, b);
+
+ // Act
+ CieLch output = Converter.ToCieLch(input);
+
+ // Assert
+ Assert.Equal(l2, output.L, FloatRoundingComparer);
+ Assert.Equal(c, output.C, FloatRoundingComparer);
+ Assert.Equal(h, output.H, FloatRoundingComparer);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest.cs
index 539085477..8e6088bdc 100644
--- a/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest.cs
+++ b/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndCieLabConversionTest.cs
@@ -11,7 +11,7 @@
///
///
/// Test data generated using:
- /// http://www.brucelindbloom.com/index.html?ColorCalculator.html
+ ///
///
public class CieXyzAndCieLabConversionTest
{
@@ -29,7 +29,7 @@
[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)
+ 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);
@@ -54,7 +54,7 @@
[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)
+ 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);
diff --git a/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndHunterLabConversionTest.cs b/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndHunterLabConversionTest.cs
index 575e330d3..b5e482f61 100644
--- a/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndHunterLabConversionTest.cs
+++ b/tests/ImageSharp.Tests/Colors/Colorspaces/CieXyzAndHunterLabConversionTest.cs
@@ -11,7 +11,7 @@
///
///
/// Test data generated using:
- /// http://www.brucelindbloom.com/index.html?ColorCalculator.html
+ ///
///
public class CieXyzAndHunterLabConversionTest
{
@@ -23,7 +23,7 @@
[Theory]
[InlineData(0, 0, 0, 0, 0, 0)]
[InlineData(100, 0, 0, 0.98074, 1, 1.18232)] // C white point is HunterLab 100, 0, 0
- public void Convert_HunterLab_to_XYZ(float l, float a, float b, float x, float y, float z)
+ public void Convert_HunterLab_to_Xyz(float l, float a, float b, float x, float y, float z)
{
// Arrange
HunterLab input = new HunterLab(l, a, b);
@@ -44,7 +44,7 @@
[Theory]
[InlineData(0, 0, 0, 0, 0, 0)]
[InlineData(100, 0, 0, 0.95047, 1, 1.08883)] // D65 white point is HunerLab 100, 0, 0 (adaptation to C performed)
- public void Convert_HunterLab_to_XYZ_D65(float l, float a, float b, float x, float y, float z)
+ public void Convert_HunterLab_to_Xyz_D65(float l, float a, float b, float x, float y, float z)
{
// Arrange
HunterLab input = new HunterLab(l, a, b);
@@ -65,7 +65,7 @@
[Theory]
[InlineData(0, 0, 0, 0, 0, 0)]
[InlineData(0.95047, 1, 1.08883, 100, 0, 0)] // D65 white point is HunterLab 100, 0, 0 (adaptation to C performed)
- public void Convert_XYZ_D65_to_HunterLab(float x, float y, float z, float l, float a, float b)
+ public void Convert_Xyz_D65_to_HunterLab(float x, float y, float z, float l, float a, float b)
{
// Arrange
CieXyz input = new CieXyz(x, y, z);
diff --git a/tests/ImageSharp.Tests/Colors/Colorspaces/ColorConverterAdaptTest.cs b/tests/ImageSharp.Tests/Colors/Colorspaces/ColorConverterAdaptTest.cs
index ac9a87ce1..a5b0f7ea9 100644
--- a/tests/ImageSharp.Tests/Colors/Colorspaces/ColorConverterAdaptTest.cs
+++ b/tests/ImageSharp.Tests/Colors/Colorspaces/ColorConverterAdaptTest.cs
@@ -8,6 +8,12 @@ namespace ImageSharp.Tests
using Xunit;
+ ///
+ /// Tests methods.
+ /// Test data generated using:
+ ///
+ ///
+ ///
public class ColorConverterAdaptTest
{
private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(3);
@@ -76,7 +82,7 @@ namespace ImageSharp.Tests
[Theory]
[InlineData(0, 0, 0, 0, 0, 0)]
[InlineData(0.5, 0.5, 0.5, 0.510286, 0.501489, 0.378970)]
- public void Adapt_XYZ_D65_To_D50_Bradford(float x1, float y1, float z1, float x2, float y2, float z2)
+ public void Adapt_Xyz_D65_To_D50_Bradford(float x1, float y1, float z1, float x2, float y2, float z2)
{
// Arrange
CieXyz input = new CieXyz(x1, y1, z1);
@@ -105,7 +111,7 @@ namespace ImageSharp.Tests
CieXyz expectedOutput = new CieXyz(x2, y2, z2);
ColorSpaceConverter converter = new ColorSpaceConverter
{
- ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XYZScaling),
+ ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling),
WhitePoint = Illuminants.D50
};
@@ -121,14 +127,14 @@ namespace ImageSharp.Tests
[Theory]
[InlineData(0, 0, 0, 0, 0, 0)]
[InlineData(0.5, 0.5, 0.5, 0.507233, 0.500000, 0.378943)]
- public void Adapt_XYZ_D65_To_D50_XYZScaling(float x1, float y1, float z1, float x2, float y2, float z2)
+ public void Adapt_Xyz_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);
ColorSpaceConverter converter = new ColorSpaceConverter
{
- ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XYZScaling),
+ ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling),
WhitePoint = Illuminants.D50
};
diff --git a/tests/ImageSharp.Tests/Helpers/MathFTests.cs b/tests/ImageSharp.Tests/Helpers/MathFTests.cs
index 7f3fb77d0..f381f2a77 100644
--- a/tests/ImageSharp.Tests/Helpers/MathFTests.cs
+++ b/tests/ImageSharp.Tests/Helpers/MathFTests.cs
@@ -18,12 +18,24 @@
Assert.Equal(MathF.Ceiling(0.3333F), (float)Math.Ceiling(0.3333F));
}
+ [Fact]
+ public void MathF_Cos_Is_Equal()
+ {
+ Assert.Equal(MathF.Cos(0.3333F), (float)Math.Cos(0.3333F));
+ }
+
[Fact]
public void MathF_Abs_Is_Equal()
{
Assert.Equal(MathF.Abs(-0.3333F), (float)Math.Abs(-0.3333F));
}
+ [Fact]
+ public void MathF_Atan2_Is_Equal()
+ {
+ Assert.Equal(MathF.Atan2(1.2345F, 1.2345F), (float)Math.Atan2(1.2345F, 1.2345F));
+ }
+
[Fact]
public void MathF_Exp_Is_Equal()
{
@@ -65,5 +77,17 @@
{
Assert.Equal(MathF.Sqrt(2F), (float)Math.Sqrt(2F));
}
+
+ [Fact]
+ public void Convert_Degree_To_Radian()
+ {
+ Assert.Equal((float)(Math.PI / 2D), MathF.DegreeToRadian(90F), new FloatRoundingComparer(6));
+ }
+
+ [Fact]
+ public void Convert_Radian_To_Degree()
+ {
+ Assert.Equal(60F, MathF.RadianToDegree((float)(Math.PI / 3D)), new FloatRoundingComparer(5));
+ }
}
}
\ No newline at end of file