diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs new file mode 100644 index 000000000..2c8327462 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/CieLchuv.cs @@ -0,0 +1,247 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.ColorSpaces +{ + using System; + using System.ComponentModel; + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. + /// + /// + public struct CieLchuv : IColorVector, IEquatable, IAlmostEquatable + { + /// + /// D50 standard illuminant. + /// Used when reference white is not specified explicitly. + /// + public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; + + /// + /// Represents a that has L, C, H values set to zero. + /// + public static readonly CieLchuv Empty = default(CieLchuv); + + /// + /// 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. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLchuv(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. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLchuv(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. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLchuv(Vector3 vector) + : this(vector, DefaultWhitePoint) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l, c, h components. + /// The reference white point. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLchuv(Vector3 vector, CieXyz whitePoint) + : this() + { + this.backingVector = vector; + this.WhitePoint = whitePoint; + } + + /// + /// Gets the reference white point of this color + /// + public CieXyz WhitePoint + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } + + /// + /// Gets the lightness dimension. + /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public float L + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.backingVector.X; + } + + /// + /// Gets the a chroma component. + /// A value ranging from 0 to 100. + /// + public float C + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.backingVector.Y; + } + + /// + /// Gets the h° hue component in degrees. + /// A value ranging from 0 to 360. + /// + public float H + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.backingVector.Z; + } + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + public Vector3 Vector + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => 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. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(CieLchuv left, CieLchuv 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. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(CieLchuv left, CieLchuv right) + { + return !left.Equals(right); + } + + /// + public override int GetHashCode() + { + unchecked + { + int hashCode = this.WhitePoint.GetHashCode(); + hashCode = (hashCode * 397) ^ this.backingVector.GetHashCode(); + return hashCode; + } + } + + /// + public override string ToString() + { + if (this.IsEmpty) + { + return "CieLchuv [Empty]"; + } + + return $"CieLchuv [ L={this.L:#0.##}, C={this.C:#0.##}, H={this.H:#0.##}]"; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + if (obj is CieLchuv) + { + return this.Equals((CieLchuv)obj); + } + + return false; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLchuv other) + { + return this.backingVector.Equals(other.backingVector) + && this.WhitePoint.Equals(other.WhitePoint); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool AlmostEquals(CieLchuv other, float precision) + { + Vector3 result = Vector3.Abs(this.backingVector - other.backingVector); + + return this.WhitePoint.Equals(other.WhitePoint) + && 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 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs index f51ce06e6..15b972b45 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs @@ -10,9 +10,9 @@ namespace ImageSharp.ColorSpaces.Conversion using ImageSharp.ColorSpaces; using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Performs chromatic adaptation on the various color spaces. + /// public partial class ColorSpaceConverter { /// @@ -81,6 +81,29 @@ namespace ImageSharp.ColorSpaces.Conversion return this.ToCieLch(labColor); } + /// + /// Adapts color from the source white point to white point set in . + /// + /// The color to adapt + /// The adapted color + public CieLchuv Adapt(CieLchuv 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; + } + + CieLuv luvColor = this.ToCieLuv(color); + return this.ToCieLchuv(luvColor); + } + /// /// Adapts color from the source white point to white point set in . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs index 9f6daf7af..399bcce08 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs @@ -9,9 +9,9 @@ namespace ImageSharp.ColorSpaces.Conversion using ImageSharp.ColorSpaces.Conversion.Implementation.CieLab; using ImageSharp.ColorSpaces.Conversion.Implementation.CieLch; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { /// @@ -40,6 +40,19 @@ namespace ImageSharp.ColorSpaces.Conversion return this.Adapt(unadapted); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLab ToCieLab(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs index ba7b4fed9..0be28f5d6 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs @@ -7,9 +7,9 @@ namespace ImageSharp.ColorSpaces.Conversion { using ImageSharp.ColorSpaces.Conversion.Implementation.CieLch; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { /// @@ -33,6 +33,19 @@ namespace ImageSharp.ColorSpaces.Conversion return CieLabToCieLchConverter.Convert(adapted); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLch ToCieLch(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs new file mode 100644 index 000000000..4a176e459 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs @@ -0,0 +1,192 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.ColorSpaces.Conversion +{ + using ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv; + + /// + /// Allows conversion to . + /// + public partial class ColorSpaceConverter + { + /// + /// The converter for converting between CieLab to CieLchuv. + /// + private static readonly CieLuvToCieLchuvConverter CieLuvToCieLchuvConverter = new CieLuvToCieLchuvConverter(); + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(CieLab color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(CieLch color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(CieLuv color) + { + Guard.NotNull(color, nameof(color)); + + // Adaptation + CieLuv adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color; + + // Conversion + return CieLuvToCieLchuvConverter.Convert(adapted); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(CieXyy color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(CieXyz color) + { + Guard.NotNull(color, nameof(color)); + + CieLab labColor = this.ToCieLab(color); + return this.ToCieLchuv(labColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(Cmyk color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(Hsl color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(Hsv color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(HunterLab color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(LinearRgb color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(Lms color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(Rgb color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLchuv ToCieLchuv(YCbCr color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs index 7012116dc..f53c565d2 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs @@ -6,13 +6,16 @@ namespace ImageSharp.ColorSpaces.Conversion { using ImageSharp.ColorSpaces; + using ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv; using ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { + private static readonly CieLchuvToCieLuvConverter CieLchuvToCieLuvConverter = new CieLchuvToCieLuvConverter(); + /// /// Converts a into a /// @@ -39,6 +42,27 @@ namespace ImageSharp.ColorSpaces.Conversion return this.ToCieLuv(xyzColor); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLuv ToCieLuv(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + // Conversion (perserving white point) + CieLuv unadapted = CieLchuvToCieLuvConverter.Convert(color); + + if (!this.IsChromaticAdaptationPerformed) + { + return unadapted; + } + + // Adaptation + return this.Adapt(unadapted); + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs index 25055f446..fff93b210 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs @@ -7,9 +7,9 @@ namespace ImageSharp.ColorSpaces.Conversion { using ImageSharp.ColorSpaces.Conversion.Implementation.CieXyy; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { private static readonly CieXyzAndCieXyyConverter CieXyzAndCieXyyConverter = new CieXyzAndCieXyyConverter(); @@ -42,6 +42,20 @@ namespace ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieXyy ToCieXyy(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + + return this.ToCieXyy(xyzColor); + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs index e21c7afc7..b56c3df88 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs @@ -11,9 +11,9 @@ namespace ImageSharp.ColorSpaces.Conversion using ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab; using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { private static readonly CieLabToCieXyzConverter CieLabToCieXyzConverter = new CieLabToCieXyzConverter(); @@ -60,6 +60,23 @@ namespace ImageSharp.ColorSpaces.Conversion return this.ToCieXyz(labColor); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieXyz ToCieXyz(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + // Conversion to Luv + CieLuv luvColor = CieLchuvToCieLuvConverter.Convert(color); + + // Conversion to XYZ (incl. adaptation) + CieXyz result = this.ToCieXyz(luvColor); + return result; + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs index 377d7e290..d67f5c068 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs @@ -8,9 +8,9 @@ namespace ImageSharp.ColorSpaces.Conversion using ImageSharp.ColorSpaces; using ImageSharp.ColorSpaces.Conversion.Implementation.Cmyk; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { private static readonly CmykAndRgbConverter CmykAndRgbConverter = new CmykAndRgbConverter(); @@ -43,6 +43,20 @@ namespace ImageSharp.ColorSpaces.Conversion return this.ToCmyk(xyzColor); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Cmyk ToCmyk(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + + return this.ToCmyk(xyzColor); + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs index a35f27f34..dd8b9a2bc 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs @@ -8,9 +8,9 @@ namespace ImageSharp.ColorSpaces.Conversion using ImageSharp.ColorSpaces; using ImageSharp.ColorSpaces.Conversion.Implementation.Hsl; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { private static readonly HslAndRgbConverter HslAndRgbConverter = new HslAndRgbConverter(); @@ -43,6 +43,20 @@ namespace ImageSharp.ColorSpaces.Conversion return this.ToHsl(xyzColor); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Hsl ToHsl(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + + return this.ToHsl(xyzColor); + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs index e3c2e0c1d..5df05c1d7 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs @@ -8,9 +8,9 @@ namespace ImageSharp.ColorSpaces.Conversion using ImageSharp.ColorSpaces; using ImageSharp.ColorSpaces.Conversion.Implementation.Hsv; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { private static readonly HsvAndRgbConverter HsvAndRgbConverter = new HsvAndRgbConverter(); @@ -43,6 +43,20 @@ namespace ImageSharp.ColorSpaces.Conversion return this.ToHsv(xyzColor); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Hsv ToHsv(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + + return this.ToHsv(xyzColor); + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs index c13a6ea84..40554ec4f 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs @@ -7,9 +7,9 @@ namespace ImageSharp.ColorSpaces.Conversion { using ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { /// @@ -38,6 +38,19 @@ namespace ImageSharp.ColorSpaces.Conversion return this.ToHunterLab(xyzColor); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public HunterLab ToHunterLab(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToHunterLab(xyzColor); + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs index 0cc930e08..68c75ecee 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs @@ -7,9 +7,9 @@ namespace ImageSharp.ColorSpaces.Conversion { using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { private static readonly RgbToLinearRgbConverter RgbToLinearRgbConverter = new RgbToLinearRgbConverter(); @@ -42,6 +42,19 @@ namespace ImageSharp.ColorSpaces.Conversion return this.ToLinearRgb(xyzColor); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public LinearRgb ToLinearRgb(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToLinearRgb(xyzColor); + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs index 2b0979a02..1ad5b79d6 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs @@ -7,9 +7,9 @@ namespace ImageSharp.ColorSpaces.Conversion { using ImageSharp.ColorSpaces; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { /// @@ -38,6 +38,19 @@ namespace ImageSharp.ColorSpaces.Conversion return this.ToLms(xyzColor); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Lms ToLms(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToLms(xyzColor); + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs index 5f669fbcf..81b2f32f6 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs @@ -7,9 +7,9 @@ namespace ImageSharp.ColorSpaces.Conversion { using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { private static readonly LinearRgbToRgbConverter LinearRgbToRgbConverter = new LinearRgbToRgbConverter(); @@ -40,6 +40,19 @@ namespace ImageSharp.ColorSpaces.Conversion return this.ToRgb(xyzColor); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Rgb ToRgb(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToRgb(xyzColor); + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs index cfd71081b..c6062e1a9 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs @@ -8,9 +8,9 @@ namespace ImageSharp.ColorSpaces.Conversion using ImageSharp.ColorSpaces; using ImageSharp.ColorSpaces.Conversion.Implementation.YCbCr; - /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. - /// + /// + /// Allows conversion to . + /// public partial class ColorSpaceConverter { private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter(); @@ -43,6 +43,20 @@ namespace ImageSharp.ColorSpaces.Conversion return this.ToYCbCr(xyzColor); } + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public YCbCr ToYCbCr(CieLchuv color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + + return this.ToYCbCr(xyzColor); + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs index 92644a85e..094d64b58 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs @@ -80,10 +80,7 @@ namespace ImageSharp.ColorSpaces.Conversion /// public Matrix4x4 LmsAdaptationMatrix { - get - { - return this.transformationMatrix; - } + get => this.transformationMatrix; set { diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs index 4cde7e330..5b63b167e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs @@ -12,7 +12,7 @@ namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLch /// /// Converts from to . /// - public class CieLchToCieLabConverter : IColorConversion + internal class CieLchToCieLabConverter : IColorConversion { /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -20,7 +20,8 @@ namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLch { DebugGuard.NotNull(input, nameof(input)); - // Conversion algorithm described here: https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC + // 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); diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs index 502c9337a..b93dce75a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs @@ -20,7 +20,8 @@ namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLch { DebugGuard.NotNull(input, nameof(input)); - // Conversion algorithm described here: https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC + // 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); diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs new file mode 100644 index 000000000..3e399016a --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv +{ + using System.Runtime.CompilerServices; + + using ImageSharp.ColorSpaces; + + /// + /// Converts from to . + /// + internal class CieLchuvToCieLuvConverter : IColorConversion + { + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLuv Convert(CieLchuv input) + { + DebugGuard.NotNull(input, nameof(input)); + + // Conversion algorithm described here: + // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 + float l = input.L, c = input.C, hDegrees = input.H; + float hRadians = MathF.DegreeToRadian(hDegrees); + + float u = c * MathF.Cos(hRadians); + float v = c * MathF.Sin(hRadians); + + return new CieLuv(l, u, v, input.WhitePoint); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs new file mode 100644 index 000000000..5339f1bcd --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv +{ + using System.Runtime.CompilerServices; + + using ImageSharp.ColorSpaces; + + /// + /// Converts from to . + /// + internal class CieLuvToCieLchuvConverter : IColorConversion + { + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLchuv Convert(CieLuv input) + { + DebugGuard.NotNull(input, nameof(input)); + + // Conversion algorithm described here: + // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 + float l = input.L, a = input.U, b = input.V; + float c = MathF.Sqrt((a * a) + (b * b)); + float hRadians = MathF.Atan2(b, a); + float hDegrees = MathF.RadianToDegree(hRadians); + + // Wrap the angle round at 360. + hDegrees = hDegrees % 360; + + // Make sure it's not negative. + while (hDegrees < 0) + { + hDegrees += 360; + } + + return new CieLchuv(l, c, hDegrees, input.WhitePoint); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs index 1248b0c7f..f2d8c92d2 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs @@ -47,7 +47,7 @@ } /// - /// Tests conversion from to . + /// Tests conversion from to . /// [Theory] [InlineData(0, 0, 0, 0, 0, 0)] diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs new file mode 100644 index 000000000..d263bbe18 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs @@ -0,0 +1,77 @@ +namespace ImageSharp.Tests.Colorspaces +{ + using System.Collections.Generic; + using ImageSharp.ColorSpaces; + using ImageSharp.ColorSpaces.Conversion; + + using Xunit; + + /// + /// Tests - conversions. + /// + /// + /// Test data generated using: + /// + /// + public class CieLuvAndCieLchuvuvConversionTests + { + 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_Lchuv_to_Luv(float l, float c, float h, float l2, float u, float v) + { + // Arrange + CieLchuv input = new CieLchuv(l, c, h); + + // Act + CieLuv output = Converter.ToCieLuv(input); + + // Assert + Assert.Equal(l2, output.L, FloatRoundingComparer); + Assert.Equal(u, output.U, FloatRoundingComparer); + Assert.Equal(v, output.V, 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)] + [InlineData(37.3511, 24.1720, 16.0684, 37.3511, 29.0255, 33.6141)] + public void Convert_Luv_to_LCHuv(float l, float u, float v, float l2, float c, float h) + { + // Arrange + CieLuv input = new CieLuv(l, u, v); + + // Act + CieLchuv output = Converter.ToCieLchuv(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/Colorspaces/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs index 7e4587045..9bd8b17c7 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Tests +namespace ImageSharp.Tests.Colorspaces { using System.Collections.Generic; using ImageSharp.ColorSpaces; diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs index a93cf19c8..5589e9753 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Tests +namespace ImageSharp.Tests.Colorspaces { using System.Collections.Generic; using ImageSharp.ColorSpaces; diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs index 1f874980d..5ad224eaf 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Tests +namespace ImageSharp.Tests.Colorspaces { using System.Collections.Generic; using ImageSharp.ColorSpaces; diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs index 2b244d1b3..868d42975 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Tests +namespace ImageSharp.Tests.Colorspaces { using System.Collections.Generic; using ImageSharp.ColorSpaces; diff --git a/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs b/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs index 6c03028c1..02095b31f 100644 --- a/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Tests +namespace ImageSharp.Tests.Colorspaces { using System.Collections.Generic; diff --git a/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs b/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs index 07c726733..dcb4113d4 100644 --- a/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Tests.Colors +namespace ImageSharp.Tests.Colorspaces { using System; using System.Numerics; @@ -21,6 +21,7 @@ namespace ImageSharp.Tests.Colors { CieLab.Empty, CieLch.Empty, + CieLchuv.Empty, CieLuv.Empty, CieXyz.Empty, CieXyy.Empty, @@ -38,6 +39,7 @@ namespace ImageSharp.Tests.Colors { { new CieLab(Vector3.One), new CieLab(Vector3.One), typeof(CieLab) }, { new CieLch(Vector3.One), new CieLch(Vector3.One), typeof(CieLch) }, + { new CieLchuv(Vector3.One), new CieLchuv(Vector3.One), typeof(CieLchuv) }, { new CieLuv(Vector3.One), new CieLuv(Vector3.One), typeof(CieLuv) }, { new CieXyz(Vector3.One), new CieXyz(Vector3.One), typeof(CieXyz) }, { new CieXyy(Vector3.One), new CieXyy(Vector3.One), typeof(CieXyy) }, @@ -56,6 +58,7 @@ namespace ImageSharp.Tests.Colors // Valid object against null { new CieLab(Vector3.One), null, typeof(CieLab) }, { new CieLch(Vector3.One), null, typeof(CieLch) }, + { new CieLchuv(Vector3.One), null, typeof(CieLchuv) }, { new CieLuv(Vector3.One), null, typeof(CieLuv) }, { new CieXyz(Vector3.One), null, typeof(CieXyz) }, { new CieXyy(Vector3.One), null, typeof(CieXyy) }, @@ -73,7 +76,7 @@ namespace ImageSharp.Tests.Colors { // Valid objects of different types but not equal { new CieLab(Vector3.One), new CieLch(Vector3.Zero), null }, - { new CieLuv(Vector3.One), new CieLuv(Vector3.Zero), null }, + { new CieLuv(Vector3.One), new CieLchuv(Vector3.Zero), null }, { new CieXyz(Vector3.One), new HunterLab(Vector3.Zero), null }, { new Rgb(Vector3.One), new LinearRgb(Vector3.Zero), null }, { new Rgb(Vector3.One), new Lms(Vector3.Zero), null }, @@ -88,6 +91,7 @@ namespace ImageSharp.Tests.Colors // Valid objects of the same type but not equal { new CieLab(Vector3.One), new CieLab(Vector3.Zero), typeof(CieLab) }, { new CieLch(Vector3.One), new CieLch(Vector3.Zero), typeof(CieLch) }, + { new CieLchuv(Vector3.One), new CieLchuv(Vector3.Zero), typeof(CieLchuv) }, { new CieLuv(Vector3.One), new CieLuv(Vector3.Zero), typeof(CieLuv) }, { new CieXyz(Vector3.One), new CieXyz(Vector3.Zero), typeof(CieXyz) }, { new CieXyy(Vector3.One), new CieXyy(Vector3.Zero), typeof(CieXyy) }, @@ -112,6 +116,7 @@ namespace ImageSharp.Tests.Colors { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, .0001F), typeof(CieLab), .0001F }, { new CieLab(0F, 0F, 0F), new CieLab(.0005F, 0F, 0F), typeof(CieLab), .0005F }, { new CieLch(0F, 0F, 0F), new CieLch(0F, .001F, 0F), typeof(CieLch), .001F }, + { new CieLchuv(0F, 0F, 0F), new CieLchuv(0F, .001F, 0F), typeof(CieLchuv), .001F }, { new CieLuv(0F, 0F, 0F), new CieLuv(0F, .001F, 0F), typeof(CieLuv), .001F }, { new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380F, 380F), typeof(CieXyz), 0F }, { new CieXyz(380F, 380F, 380F), new CieXyz(380.001F, 380F, 380F), typeof(CieXyz), .01F }, diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs index 881016855..7a4520a57 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Tests +namespace ImageSharp.Tests.Colorspaces { using System.Collections.Generic; using ImageSharp.ColorSpaces;