From 7005bcd6dacbc758d2d3ec061ab6978c956b5f7c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 20 Apr 2017 18:34:21 +1000 Subject: [PATCH] Add Cmyk, organise tests --- src/ImageSharp/Colors/Spaces/Cmyk.cs | 149 ++++++++++++++++++ .../Conversion/ColorSpaceConverter.CieLab.cs | 67 ++++---- .../Conversion/ColorSpaceConverter.CieLch.cs | 68 +++++--- .../Conversion/ColorSpaceConverter.CieXyy.cs | 36 +++-- .../Conversion/ColorSpaceConverter.CieXyz.cs | 67 +++++--- .../Conversion/ColorSpaceConverter.Cmyk.cs | 128 +++++++++++++++ .../ColorSpaceConverter.HunterLab.cs | 55 ++++--- .../ColorSpaceConverter.LinearRgb.cs | 67 ++++---- .../Conversion/ColorSpaceConverter.Lms.cs | 45 ++++-- .../Conversion/ColorSpaceConverter.Rgb.cs | 63 +++++--- .../Spaces/Conversion/ColorSpaceConverter.cs | 1 + .../CieXyy/CieXyzAndCieXyyConverter.cs | 2 +- .../Cmyk/CmykAndRgbConverter.cs | 50 ++++++ .../Colorspaces/RgbAndCieXyzConversionTest.cs | 30 ++-- .../Colorspaces/RgbAndCmykConversionTest.cs | 68 ++++++++ 15 files changed, 706 insertions(+), 190 deletions(-) create mode 100644 src/ImageSharp/Colors/Spaces/Cmyk.cs create mode 100644 src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Cmyk.cs create mode 100644 src/ImageSharp/Colors/Spaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs create mode 100644 tests/ImageSharp.Tests/Colors/Colorspaces/RgbAndCmykConversionTest.cs diff --git a/src/ImageSharp/Colors/Spaces/Cmyk.cs b/src/ImageSharp/Colors/Spaces/Cmyk.cs new file mode 100644 index 000000000..488c36e8e --- /dev/null +++ b/src/ImageSharp/Colors/Spaces/Cmyk.cs @@ -0,0 +1,149 @@ +// +// 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 an CMYK (cyan, magenta, yellow, keyline) color. + /// + public struct Cmyk : IEquatable, IAlmostEquatable + { + /// + /// Represents a that has C, M, Y, and K values set to zero. + /// + public static readonly Cmyk Empty = default(Cmyk); + + /// + /// The backing vector for SIMD support. + /// + private readonly Vector4 backingVector; + + /// + /// Initializes a new instance of the struct. + /// + /// The cyan component. + /// The magenta component. + /// The yellow component. + /// The keyline black component. + public Cmyk(float c, float m, float y, float k) + : this() + { + this.backingVector = Vector4.Clamp(new Vector4(c, m, y, k), Vector4.Zero, Vector4.One); + } + + /// + /// Gets the cyan color component. + /// A value ranging between 0 and 1. + /// + public float C => this.backingVector.X; + + /// + /// Gets the magenta color component. + /// A value ranging between 0 and 1. + /// + public float M => this.backingVector.Y; + + /// + /// Gets the yellow color component. + /// A value ranging between 0 and 1. + /// + public float Y => this.backingVector.Z; + + /// + /// Gets the keyline black color component. + /// A value ranging between 0 and 1. + /// + public float K => this.backingVector.W; + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + /// 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 ==(Cmyk left, Cmyk 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 !=(Cmyk left, Cmyk right) + { + return !left.Equals(right); + } + + /// + public override int GetHashCode() + { + return this.backingVector.GetHashCode(); + } + + /// + public override string ToString() + { + if (this.IsEmpty) + { + return "Cmyk [Empty]"; + } + + return $"Cmyk [ C={this.C:#0.##}, M={this.M:#0.##}, Y={this.Y:#0.##}, K={this.K:#0.##}]"; + } + + /// + public override bool Equals(object obj) + { + if (obj is Cmyk) + { + return this.Equals((Cmyk)obj); + } + + return false; + } + + /// + public bool Equals(Cmyk other) + { + return this.backingVector.Equals(other.backingVector); + } + + /// + public bool AlmostEquals(Cmyk other, float precision) + { + Vector4 result = Vector4.Abs(this.backingVector - other.backingVector); + + return result.X <= precision + && result.Y <= precision + && result.Z <= precision + && result.W <= precision; + } + } +} \ 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 dfc5fbe4a..95582d2b3 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLab.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLab.cs @@ -20,30 +20,32 @@ namespace ImageSharp.Colors.Spaces.Conversion private static readonly CieLchToCieLabConverter CieLchToCieLabConverter = new CieLchToCieLabConverter(); /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieLab ToCieLab(CieXyz color) + public CieLab ToCieLab(CieLch 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 (perserving white point) + CieLab unadapted = CieLchToCieLabConverter.Convert(color); - // Conversion - CieXyzToCieLabConverter converter = new CieXyzToCieLabConverter(this.TargetLabWhitePoint); - return converter.Convert(adapted); + if (!this.IsChromaticAdaptationPerformed) + { + return unadapted; + } + + // Adaptation + return this.Adapt(unadapted); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieLab ToCieLab(LinearRgb color) + public CieLab ToCieLab(CieXyy color) { Guard.NotNull(color, nameof(color)); @@ -52,11 +54,30 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieLab ToCieLab(Rgb color) + 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); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieLab ToCieLab(Cmyk color) { Guard.NotNull(color, nameof(color)); @@ -91,11 +112,11 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieLab ToCieLab(CieXyy color) + public CieLab ToCieLab(LinearRgb color) { Guard.NotNull(color, nameof(color)); @@ -104,24 +125,16 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieLab ToCieLab(CieLch color) + public CieLab ToCieLab(Rgb 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); + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); } } } \ 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 index 0ad1f53d2..79fa53c82 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLch.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieLch.cs @@ -17,6 +17,35 @@ namespace ImageSharp.Colors.Spaces.Conversion /// private static readonly CieLabToCieLchConverter CieLabToCieLchConverter = new CieLabToCieLchConverter(); + /// + /// 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(CieXyy color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); + } + /// /// Converts a into a /// @@ -31,45 +60,42 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieLch ToCieLch(Rgb color) + public CieLch ToCieLch(Cmyk color) { Guard.NotNull(color, nameof(color)); - CieLab labColor = this.ToCieLab(color); - return this.ToCieLch(labColor); + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieLch ToCieLch(LinearRgb color) + public CieLch ToCieLch(HunterLab color) { Guard.NotNull(color, nameof(color)); - CieLab labColor = this.ToCieLab(color); - return this.ToCieLch(labColor); + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieLch ToCieLch(CieLab color) + public CieLch ToCieLch(LinearRgb color) { Guard.NotNull(color, nameof(color)); - // Adaptation - CieLab adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color; - - // Conversion - return CieLabToCieLchConverter.Convert(adapted); + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } /// @@ -81,21 +107,21 @@ namespace ImageSharp.Colors.Spaces.Conversion { Guard.NotNull(color, nameof(color)); - CieLab labColor = this.ToCieLab(color); - return this.ToCieLch(labColor); + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieLch ToCieLch(HunterLab color) + public CieLch ToCieLch(Rgb color) { Guard.NotNull(color, nameof(color)); - CieLab labColor = this.ToCieLab(color); - return this.ToCieLch(labColor); + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } } } \ No newline at end of file diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyy.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyy.cs index 692b13f94..0b86109af 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyy.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyy.cs @@ -15,23 +15,25 @@ namespace ImageSharp.Colors.Spaces.Conversion private static readonly CieXyzAndCieXyyConverter CieXyzAndCieXyyConverter = new CieXyzAndCieXyyConverter(); /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieXyy ToCieXyy(CieXyz color) + public CieXyy ToCieXyy(CieLab color) { Guard.NotNull(color, nameof(color)); - return CieXyzAndCieXyyConverter.Convert(color); + CieXyz xyzColor = this.ToCieXyz(color); + + return this.ToCieXyy(xyzColor); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieXyy ToCieXyy(CieLab color) + public CieXyy ToCieXyy(CieLch color) { Guard.NotNull(color, nameof(color)); @@ -41,11 +43,23 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieXyy ToCieXyy(CieLch color) + public CieXyy ToCieXyy(CieXyz color) + { + Guard.NotNull(color, nameof(color)); + + return CieXyzAndCieXyyConverter.Convert(color); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public CieXyy ToCieXyy(Cmyk color) { Guard.NotNull(color, nameof(color)); @@ -83,11 +97,11 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieXyy ToCieXyy(Rgb color) + public CieXyy ToCieXyy(Lms color) { Guard.NotNull(color, nameof(color)); @@ -97,11 +111,11 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieXyy ToCieXyy(Lms color) + public CieXyy ToCieXyy(Rgb color) { Guard.NotNull(color, nameof(color)); diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs index c04f1093b..7e171d42e 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.CieXyz.cs @@ -41,6 +41,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); + } + /// /// Converts a into a /// @@ -55,30 +71,38 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieXyz ToCieXyz(Lms color) + public CieXyz ToCieXyz(Cmyk color) { Guard.NotNull(color, nameof(color)); // Conversion - return this.cachedCieXyzAndLmsConverter.Convert(color); + Rgb rgb = this.ToRgb(color); + + return this.ToCieXyz(rgb); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieXyz ToCieXyz(Rgb color) + public CieXyz ToCieXyz(HunterLab color) { Guard.NotNull(color, nameof(color)); // Conversion - LinearRgb linear = RgbToLinearRgbConverter.Convert(color); - return this.ToCieXyz(linear); + CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color); + + // Adaptation + CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed + ? unadapted + : this.Adapt(unadapted, color.WhitePoint); + + return adapted; } /// @@ -96,44 +120,35 @@ namespace ImageSharp.Colors.Spaces.Conversion // Adaptation return color.WorkingSpace.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed - ? unadapted - : this.Adapt(unadapted, color.WorkingSpace.WhitePoint); + ? unadapted + : this.Adapt(unadapted, color.WorkingSpace.WhitePoint); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieXyz ToCieXyz(HunterLab color) + public CieXyz ToCieXyz(Lms color) { Guard.NotNull(color, nameof(color)); // Conversion - CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color); - - // Adaptation - CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed - ? unadapted - : this.Adapt(unadapted, color.WhitePoint); - - return adapted; + return this.cachedCieXyzAndLmsConverter.Convert(color); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public CieXyz ToCieXyz(CieLch color) + public CieXyz ToCieXyz(Rgb color) { Guard.NotNull(color, nameof(color)); - // Conversion to Lab - CieLab labColor = CieLchToCieLabConverter.Convert(color); - - // Conversion to XYZ (incl. adaptation) - return this.ToCieXyz(labColor); + // Conversion + LinearRgb linear = RgbToLinearRgbConverter.Convert(color); + return this.ToCieXyz(linear); } /// diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Cmyk.cs new file mode 100644 index 000000000..dc7e10927 --- /dev/null +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Cmyk.cs @@ -0,0 +1,128 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Colors.Spaces.Conversion +{ + using ImageSharp.Colors.Spaces; + using ImageSharp.Colors.Spaces.Conversion.Implementation.Cmyk; + + /// + /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. + /// + public partial class ColorSpaceConverter + { + private static readonly CmykAndRgbConverter CmykAndRgbConverter = new CmykAndRgbConverter(); + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Cmyk ToCmyk(CieLab color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + + return this.ToCmyk(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Cmyk ToCmyk(CieLch color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + + return this.ToCmyk(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Cmyk ToCmyk(CieXyy color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + + return this.ToCmyk(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Cmyk ToCmyk(CieXyz color) + { + Guard.NotNull(color, nameof(color)); + + Rgb rgb = this.ToRgb(color); + + return CmykAndRgbConverter.Convert(rgb); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Cmyk ToCmyk(HunterLab color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + + return this.ToCmyk(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Cmyk ToCmyk(LinearRgb color) + { + Guard.NotNull(color, nameof(color)); + + Rgb rgb = this.ToRgb(color); + + return CmykAndRgbConverter.Convert(rgb); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Cmyk ToCmyk(Lms color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + + return this.ToCmyk(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Cmyk ToCmyk(Rgb color) + { + Guard.NotNull(color, nameof(color)); + + return CmykAndRgbConverter.Convert(color); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs index 961b43fd2..6d8442387 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.HunterLab.cs @@ -13,29 +13,24 @@ namespace ImageSharp.Colors.Spaces.Conversion public partial class ColorSpaceConverter { /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public HunterLab ToHunterLab(CieXyz color) + public HunterLab ToHunterLab(CieLab color) { Guard.NotNull(color, nameof(color)); - // Adaptation - CieXyz adapted = !this.WhitePoint.Equals(this.TargetHunterLabWhitePoint) && this.IsChromaticAdaptationPerformed - ? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetHunterLabWhitePoint) - : color; - - // Conversion - return new CieXyzToHunterLabConverter(this.TargetHunterLabWhitePoint).Convert(adapted); + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToHunterLab(xyzColor); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public HunterLab ToHunterLab(Rgb color) + public HunterLab ToHunterLab(CieLch color) { Guard.NotNull(color, nameof(color)); @@ -44,11 +39,11 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public HunterLab ToHunterLab(LinearRgb color) + public HunterLab ToHunterLab(CieXyy color) { Guard.NotNull(color, nameof(color)); @@ -57,11 +52,29 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public HunterLab ToHunterLab(CieLab color) + public HunterLab ToHunterLab(CieXyz color) + { + Guard.NotNull(color, nameof(color)); + + // Adaptation + CieXyz adapted = !this.WhitePoint.Equals(this.TargetHunterLabWhitePoint) && this.IsChromaticAdaptationPerformed + ? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetHunterLabWhitePoint) + : color; + + // Conversion + return new CieXyzToHunterLabConverter(this.TargetHunterLabWhitePoint).Convert(adapted); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public HunterLab ToHunterLab(Cmyk color) { Guard.NotNull(color, nameof(color)); @@ -70,11 +83,11 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public HunterLab ToHunterLab(Lms color) + public HunterLab ToHunterLab(LinearRgb color) { Guard.NotNull(color, nameof(color)); @@ -83,11 +96,11 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public HunterLab ToHunterLab(CieLch color) + public HunterLab ToHunterLab(Lms color) { Guard.NotNull(color, nameof(color)); @@ -96,11 +109,11 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public HunterLab ToHunterLab(CieXyy color) + public HunterLab ToHunterLab(Rgb color) { Guard.NotNull(color, nameof(color)); diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs index c7389918e..d381634d2 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.LinearRgb.cs @@ -17,16 +17,42 @@ namespace ImageSharp.Colors.Spaces.Conversion private CieXyzToLinearRgbConverter cieXyzToLinearRgbConverter; /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public LinearRgb ToLinearRgb(Rgb color) + public LinearRgb ToLinearRgb(CieLab color) { Guard.NotNull(color, nameof(color)); - // Conversion - return RgbToLinearRgbConverter.Convert(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)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToLinearRgb(xyzColor); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public LinearRgb ToLinearRgb(CieXyy color) + { + Guard.NotNull(color, nameof(color)); + + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToLinearRgb(xyzColor); } /// @@ -49,24 +75,24 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public LinearRgb ToLinearRgb(HunterLab color) + public LinearRgb ToLinearRgb(Cmyk color) { Guard.NotNull(color, nameof(color)); - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); + Rgb rgb = this.ToRgb(color); + return this.ToLinearRgb(rgb); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public LinearRgb ToLinearRgb(CieLab color) + public LinearRgb ToLinearRgb(HunterLab color) { Guard.NotNull(color, nameof(color)); @@ -88,29 +114,16 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(CieLch color) - { - Guard.NotNull(color, nameof(color)); - - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public LinearRgb ToLinearRgb(CieXyy color) + public LinearRgb ToLinearRgb(Rgb color) { Guard.NotNull(color, nameof(color)); - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); + // Conversion + return RgbToLinearRgbConverter.Convert(color); } /// diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs index 74a6dd639..202ecf4dd 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Lms.cs @@ -13,24 +13,24 @@ namespace ImageSharp.Colors.Spaces.Conversion public partial class ColorSpaceConverter { /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Lms ToLms(CieXyz color) + public Lms ToLms(CieLab color) { Guard.NotNull(color, nameof(color)); - // Conversion - return this.cachedCieXyzAndLmsConverter.Convert(color); + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToLms(xyzColor); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Lms ToLms(CieLab color) + public Lms ToLms(CieLch color) { Guard.NotNull(color, nameof(color)); @@ -39,11 +39,11 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Lms ToLms(HunterLab color) + public Lms ToLms(CieXyy color) { Guard.NotNull(color, nameof(color)); @@ -52,11 +52,24 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Lms ToLms(LinearRgb color) + public Lms ToLms(CieXyz color) + { + Guard.NotNull(color, nameof(color)); + + // Conversion + return this.cachedCieXyzAndLmsConverter.Convert(color); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Lms ToLms(Cmyk color) { Guard.NotNull(color, nameof(color)); @@ -65,11 +78,11 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Lms ToLms(Rgb color) + public Lms ToLms(HunterLab color) { Guard.NotNull(color, nameof(color)); @@ -78,11 +91,11 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Lms ToLms(CieLch color) + public Lms ToLms(LinearRgb color) { Guard.NotNull(color, nameof(color)); @@ -91,11 +104,11 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Lms ToLms(CieXyy color) + public Lms ToLms(Rgb color) { Guard.NotNull(color, nameof(color)); diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs index 50b79bd2b..2682b05aa 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.Rgb.cs @@ -15,40 +15,37 @@ namespace ImageSharp.Colors.Spaces.Conversion private static readonly LinearRgbToRgbConverter LinearRgbToRgbConverter = new LinearRgbToRgbConverter(); /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Rgb ToRgb(LinearRgb color) + public Rgb ToRgb(CieLab color) { Guard.NotNull(color, nameof(color)); - // Conversion - return LinearRgbToRgbConverter.Convert(color); + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToRgb(xyzColor); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Rgb ToRgb(CieXyz color) + public Rgb ToRgb(CieLch color) { Guard.NotNull(color, nameof(color)); - // Conversion - LinearRgb linear = this.ToLinearRgb(color); - - // Compand - return this.ToRgb(linear); + CieXyz xyzColor = this.ToCieXyz(color); + return this.ToRgb(xyzColor); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Rgb ToRgb(HunterLab color) + public Rgb ToRgb(CieXyy color) { Guard.NotNull(color, nameof(color)); @@ -57,37 +54,40 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Rgb ToRgb(CieLab color) + public Rgb ToRgb(CieXyz color) { Guard.NotNull(color, nameof(color)); - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); + // Conversion + LinearRgb linear = this.ToLinearRgb(color); + + // Compand + return this.ToRgb(linear); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Rgb ToRgb(Lms color) + public Rgb ToRgb(Cmyk color) { Guard.NotNull(color, nameof(color)); - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); + // Conversion + return CmykAndRgbConverter.Convert(color); } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Rgb ToRgb(CieLch color) + public Rgb ToRgb(HunterLab color) { Guard.NotNull(color, nameof(color)); @@ -96,11 +96,24 @@ namespace ImageSharp.Colors.Spaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public Rgb ToRgb(CieXyy color) + public Rgb ToRgb(LinearRgb color) + { + Guard.NotNull(color, nameof(color)); + + // Conversion + return LinearRgbToRgbConverter.Convert(color); + } + + /// + /// Converts a into a + /// + /// The color to convert. + /// The + public Rgb ToRgb(Lms color) { Guard.NotNull(color, nameof(color)); diff --git a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.cs b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.cs index 8727c4e6a..2de2947e4 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/ColorSpaceConverter.cs @@ -21,6 +21,7 @@ namespace ImageSharp.Colors.Spaces.Conversion public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; private Matrix4x4 transformationMatrix; + private CieXyzAndLmsConverter cachedCieXyzAndLmsConverter; /// diff --git a/src/ImageSharp/Colors/Spaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs index dedb95ff8..9909697d6 100644 --- a/src/ImageSharp/Colors/Spaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs +++ b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/src/ImageSharp/Colors/Spaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs new file mode 100644 index 000000000..98a7d8f3b --- /dev/null +++ b/src/ImageSharp/Colors/Spaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Colors.Spaces.Conversion.Implementation.Cmyk +{ + using System; + + using ImageSharp.Colors.Spaces; + + /// + /// Color converter between CMYK and Rgb + /// + internal class CmykAndRgbConverter : IColorConversion, IColorConversion + { + /// + public Rgb Convert(Cmyk input) + { + float r = (1F - input.C) * (1F - input.K); + float g = (1F - input.M) * (1F - input.K); + float b = (1F - input.Y) * (1F - input.K); + + return new Rgb(r, g, b); + } + + /// + public Cmyk Convert(Rgb input) + { + // To CMYK + float c = 1F - input.R; + float m = 1F - input.G; + float y = 1F - input.B; + + // To CMYK + float k = MathF.Min(c, MathF.Min(m, y)); + + if (Math.Abs(k - 1F) < Constants.Epsilon) + { + return new Cmyk(0, 0, 0, 1F); + } + + c = (c - k) / (1F - k); + m = (m - k) / (1F - k); + y = (y - k) / (1F - k); + + return new Cmyk(c, m, y, k); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/Colorspaces/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/Colors/Colorspaces/RgbAndCieXyzConversionTest.cs index 4f18d2621..112ef5305 100644 --- a/tests/ImageSharp.Tests/Colors/Colorspaces/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/Colors/Colorspaces/RgbAndCieXyzConversionTest.cs @@ -11,7 +11,7 @@ /// /// /// Test data generated using: - /// http://www.brucelindbloom.com/index.html?ColorCalculator.html + /// /// public class RgbAndCieXyzConversionTest { @@ -38,10 +38,10 @@ Rgb output = converter.ToRgb(input); // Assert - Assert.Equal(output.WorkingSpace, Rgb.DefaultWorkingSpace); // TODO: Change Assert.Equal to the correct order, first the expected, then the current value - Assert.Equal(output.R, r, FloatRoundingComparer); - Assert.Equal(output.G, g, FloatRoundingComparer); - Assert.Equal(output.B, b, FloatRoundingComparer); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(r, output.R, FloatRoundingComparer); + Assert.Equal(g, output.G, FloatRoundingComparer); + Assert.Equal(b, output.B, FloatRoundingComparer); } /// @@ -66,10 +66,10 @@ Rgb output = converter.ToRgb(input); // Assert - Assert.Equal(output.WorkingSpace, Rgb.DefaultWorkingSpace); - Assert.Equal(output.R, r, FloatRoundingComparer); - Assert.Equal(output.G, g, FloatRoundingComparer); - Assert.Equal(output.B, b, FloatRoundingComparer); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(r, output.R, FloatRoundingComparer); + Assert.Equal(g, output.G, FloatRoundingComparer); + Assert.Equal(b, output.B, FloatRoundingComparer); } /// @@ -93,9 +93,9 @@ CieXyz output = converter.ToCieXyz(input); // Assert - Assert.Equal(output.X, x, FloatRoundingComparer); - Assert.Equal(output.Y, y, FloatRoundingComparer); - Assert.Equal(output.Z, z, FloatRoundingComparer); + Assert.Equal(x, output.X, FloatRoundingComparer); + Assert.Equal(y, output.Y, FloatRoundingComparer); + Assert.Equal(z, output.Z, FloatRoundingComparer); } /// @@ -119,9 +119,9 @@ CieXyz output = converter.ToCieXyz(input); // Assert - Assert.Equal(output.X, x, FloatRoundingComparer); - Assert.Equal(output.Y, y, FloatRoundingComparer); - Assert.Equal(output.Z, z, FloatRoundingComparer); + Assert.Equal(x, output.X, FloatRoundingComparer); + Assert.Equal(y, output.Y, FloatRoundingComparer); + Assert.Equal(z, output.Z, FloatRoundingComparer); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/Colorspaces/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/Colors/Colorspaces/RgbAndCmykConversionTest.cs new file mode 100644 index 000000000..50e187254 --- /dev/null +++ b/tests/ImageSharp.Tests/Colors/Colorspaces/RgbAndCmykConversionTest.cs @@ -0,0 +1,68 @@ +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 RgbAndCmykConversionTest + { + private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(4); + + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(1, 1, 1, 1, 0, 0, 0)] + [InlineData(0, 0, 0, 0, 1, 1, 1)] + [InlineData(0, 0.84, 0.037, 0.365, 0.635, 0.1016, 0.6115)] + public void Convert_Cmyk_To_Rgb(float c, float m, float y, float k, float r, float g, float b) + { + // Arrange + Cmyk input = new Cmyk(c, m, y, k); + + // Act + Rgb output = Converter.ToRgb(input); + + // Assert + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(r, output.R, FloatRoundingComparer); + Assert.Equal(g, output.G, FloatRoundingComparer); + Assert.Equal(b, output.B, FloatRoundingComparer); + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(1, 1, 1, 0, 0, 0, 0)] + [InlineData(0, 0, 0, 0, 0, 0, 1)] + [InlineData(0.635, 0.1016, 0.6115, 0, 0.84, 0.037, 0.365)] + public void Convert_Rgb_To_Cmyk(float r, float g, float b, float c, float m, float y, float k) + { + // Arrange + Rgb input = new Rgb(r, g, b); + + // Act + Cmyk output = Converter.ToCmyk(input); + + // Assert + Assert.Equal(c, output.C, FloatRoundingComparer); + Assert.Equal(m, output.M, FloatRoundingComparer); + Assert.Equal(y, output.Y, FloatRoundingComparer); + Assert.Equal(k, output.K, FloatRoundingComparer); + } + } +}