diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs index 49c1da9f10..8e14274f66 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorSpaces { /// - /// Represents the coordinates of CIEXY chromaticity space + /// Represents the coordinates of CIEXY chromaticity space. /// public readonly struct CieXyChromaticityCoordinates : IEquatable { diff --git a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs index 13cca1582d..09b324b00f 100644 --- a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding { /// - /// Implements gamma companding + /// Implements gamma companding. /// /// /// diff --git a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs index 9e2cf8ad86..80b8aee7e7 100644 --- a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding { /// - /// Implements L* companding + /// Implements L* companding. /// /// /// For more info see: @@ -24,7 +24,7 @@ public static class LCompanding /// The representing the linear channel value. [MethodImpl(InliningOptions.ShortMethod)] public static float Expand(float channel) - => channel <= 0.08 ? 100 * channel / CieConstants.Kappa : ImageMaths.Pow3((channel + 0.16F) / 1.16F); + => channel <= 0.08F ? (100F * channel) / CieConstants.Kappa : ImageMaths.Pow3((channel + 0.16F) / 1.16F); /// /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. @@ -33,6 +33,6 @@ public static float Expand(float channel) /// The representing the nonlinear channel value. [MethodImpl(InliningOptions.ShortMethod)] public static float Compress(float channel) - => channel <= CieConstants.Epsilon ? channel * CieConstants.Kappa / 100F : MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F; + => channel <= CieConstants.Epsilon ? (channel * CieConstants.Kappa) / 100F : (1.16F * MathF.Pow(channel, 0.3333333F)) - 0.16F; } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs index a3a9121727..773fe81647 100644 --- a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs @@ -7,14 +7,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding { /// - /// Implements Rec. 2020 companding function (for 12-bits). + /// Implements Rec. 2020 companding function. /// /// /// - /// For 10-bits, companding is identical to /// public static class Rec2020Companding { + private const float Alpha = 1.09929682680944F; + private const float AlphaMinusOne = Alpha - 1F; + private const float Beta = 0.018053968510807F; + private const float InverseBeta = Beta * 4.5F; + private const float Epsilon = 1 / 0.45F; + /// /// Expands a companded channel to its linear equivalent with respect to the energy. /// @@ -22,7 +27,7 @@ public static class Rec2020Companding /// The representing the linear channel value. [MethodImpl(InliningOptions.ShortMethod)] public static float Expand(float channel) - => channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F); + => channel < InverseBeta ? channel / 4.5F : MathF.Pow((channel + AlphaMinusOne) / Alpha, Epsilon); /// /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. @@ -31,6 +36,6 @@ public static float Expand(float channel) /// The representing the nonlinear channel value. [MethodImpl(InliningOptions.ShortMethod)] public static float Compress(float channel) - => channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F; + => channel < Beta ? 4.5F * channel : (Alpha * MathF.Pow(channel, 0.45F)) - AlphaMinusOne; } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs index e2e802d08a..edc0b9763f 100644 --- a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs @@ -14,6 +14,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding /// public static class Rec709Companding { + private const float Epsilon = 1 / 0.45F; + /// /// Expands a companded channel to its linear equivalent with respect to the energy. /// @@ -21,7 +23,7 @@ public static class Rec709Companding /// The representing the linear channel value. [MethodImpl(InliningOptions.ShortMethod)] public static float Expand(float channel) - => channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F); + => channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, Epsilon); /// /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. @@ -30,6 +32,6 @@ public static float Expand(float channel) /// The representing the nonlinear channel value. [MethodImpl(InliningOptions.ShortMethod)] public static float Compress(float channel) - => channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F; + => channel < 0.018F ? 4.5F * channel : (1.099F * MathF.Pow(channel, 0.45F)) - 0.099F; } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs index 3810736197..7f4abfa7bb 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs @@ -26,10 +26,8 @@ public partial class ColorSpaceConverter /// The public CieLch ToCieLch(in CieLab color) { - // Adaptation CieLab adapted = this.Adapt(color); - // Conversion return CieLabToCieLchConverter.Convert(adapted); } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs index f33d1ddcc9..7767b7b448 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between CIE XYZ and CIE xyY + /// Color converter between CIE XYZ and CIE xyY. /// for formulas. /// internal sealed class CieXyzAndCieXyyConverter diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs index 3812cdbdd8..f166e4c14a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs @@ -28,11 +28,15 @@ public CieXyzToLinearRgbConverter() public CieXyzToLinearRgbConverter(RgbWorkingSpaceBase workingSpace) { this.TargetWorkingSpace = workingSpace; - this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace); + + // Gets the inverted Rgb -> Xyz matrix + Matrix4x4.Invert(GetRgbToCieXyzMatrix(workingSpace), out Matrix4x4 inverted); + + this.conversionMatrix = inverted; } /// - /// Gets the target working space + /// Gets the target working space. /// public RgbWorkingSpaceBase TargetWorkingSpace { get; } @@ -40,12 +44,12 @@ public CieXyzToLinearRgbConverter(RgbWorkingSpaceBase workingSpace) /// Performs the conversion from the input to an instance of type. /// /// The input color instance. - /// The converted result + /// The converted result. [MethodImpl(InliningOptions.ShortMethod)] public LinearRgb Convert(in CieXyz input) { - Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted); - var vector = Vector3.Transform(input.ToVector3(), inverted); + var vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix); + return new LinearRgb(vector, this.TargetWorkingSpace); } } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs index 29fd32905b..0700dab43a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between and + /// Color converter between and . /// internal sealed class CmykAndRgbConverter { @@ -28,7 +28,7 @@ public Rgb Convert(in Cmyk input) /// Performs the conversion from the input to an instance of type. /// /// The input color instance. - /// The converted result + /// The converted result. [MethodImpl(InliningOptions.ShortMethod)] public Cmyk Convert(in Rgb input) { diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs index 1cc055bee2..8454430935 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between and + /// Color converter between and . /// internal sealed class LinearRgbToRgbConverter { @@ -14,16 +14,15 @@ internal sealed class LinearRgbToRgbConverter /// Performs the conversion from the input to an instance of type. /// /// The input color instance. - /// The converted result + /// The converted result. [MethodImpl(InliningOptions.ShortMethod)] public Rgb Convert(in LinearRgb input) { - var vector = input.ToVector3(); - vector.X = input.WorkingSpace.Compress(vector.X); - vector.Y = input.WorkingSpace.Compress(vector.Y); - vector.Z = input.WorkingSpace.Compress(vector.Z); - - return new Rgb(vector, input.WorkingSpace); + return new Rgb( + r: input.WorkingSpace.Compress(input.R), + g: input.WorkingSpace.Compress(input.G), + b: input.WorkingSpace.Compress(input.B), + workingSpace: input.WorkingSpace); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs index 03912a421e..4ddbe42e54 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between Rgb and LinearRgb + /// Color converter between Rgb and LinearRgb. /// internal class RgbToLinearRgbConverter { @@ -14,16 +14,15 @@ internal class RgbToLinearRgbConverter /// Performs the conversion from the input to an instance of type. /// /// The input color instance. - /// The converted result + /// The converted result. [MethodImpl(InliningOptions.ShortMethod)] public LinearRgb Convert(in Rgb input) { - var vector = input.ToVector3(); - vector.X = input.WorkingSpace.Expand(vector.X); - vector.Y = input.WorkingSpace.Expand(vector.Y); - vector.Z = input.WorkingSpace.Expand(vector.Z); - - return new LinearRgb(vector, input.WorkingSpace); + return new LinearRgb( + r: input.WorkingSpace.Expand(input.R), + g: input.WorkingSpace.Expand(input.G), + b: input.WorkingSpace.Expand(input.B), + workingSpace: input.WorkingSpace); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs index 4ac3ad3cf9..ee15ffa508 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs @@ -19,7 +19,7 @@ internal sealed class YCbCrAndRgbConverter /// Performs the conversion from the input to an instance of type. /// /// The input color instance. - /// The converted result + /// The converted result. [MethodImpl(InliningOptions.ShortMethod)] public Rgb Convert(in YCbCr input) { @@ -38,7 +38,7 @@ public Rgb Convert(in YCbCr input) /// Performs the conversion from the input to an instance of type. /// /// The input color instance. - /// The converted result + /// The converted result. [MethodImpl(InliningOptions.ShortMethod)] public YCbCr Convert(in Rgb input) { diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs index 639d0b2933..c45dea3179 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation /// /// The gamma working space. /// - public class GammaWorkingSpace : RgbWorkingSpaceBase + public sealed class GammaWorkingSpace : RgbWorkingSpaceBase { /// /// Initializes a new instance of the class. diff --git a/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs b/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs index b2ee030483..e1386e1a09 100644 --- a/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; using System.Numerics; using SixLabors.ImageSharp.ColorSpaces.Companding; using Xunit; @@ -10,9 +9,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding { /// - /// Tests various companding algorithms. Numbers are hand calculated from formulas online. - /// TODO: Oddly the formula for converting to/from Rec 2020 and 709 from Wikipedia seems to cause the value to - /// fail a round trip. They're large spaces so this is a surprise. More reading required!! + /// Tests various companding algorithms. Expanded numbers are hand calculated from formulas online. /// public class CompandingTests { @@ -24,7 +21,7 @@ public void Rec2020Companding_IsCorrect() const float input = .667F; float e = Rec2020Companding.Expand(input); float c = Rec2020Companding.Compress(e); - CompandingIsCorrectImpl(e, c, .4484759F, .3937096F); + CompandingIsCorrectImpl(e, c, .4484759F, input); } [Fact] @@ -33,7 +30,7 @@ public void Rec709Companding_IsCorrect() const float input = .667F; float e = Rec709Companding.Expand(input); float c = Rec709Companding.Compress(e); - CompandingIsCorrectImpl(e, c, .4483577F, .3937451F); + CompandingIsCorrectImpl(e, c, .4483577F, input); } [Fact] @@ -42,7 +39,7 @@ public void SRgbCompanding_IsCorrect() const float input = .667F; float e = SRgbCompanding.Expand(input); float c = SRgbCompanding.Compress(e); - CompandingIsCorrectImpl(e, c, .40242353F, .667F); + CompandingIsCorrectImpl(e, c, .40242353F, input); } [Theory] @@ -98,7 +95,7 @@ public void GammaCompanding_IsCorrect() const float input = .667F; float e = GammaCompanding.Expand(input, gamma); float c = GammaCompanding.Compress(e, gamma); - CompandingIsCorrectImpl(e, c, .41027668F, .667F); + CompandingIsCorrectImpl(e, c, .41027668F, input); } [Fact] @@ -107,7 +104,7 @@ public void LCompanding_IsCorrect() const float input = .667F; float e = LCompanding.Expand(input); float c = LCompanding.Compress(e); - CompandingIsCorrectImpl(e, c, .36236193F, .58908917F); + CompandingIsCorrectImpl(e, c, .36236193F, input); } private static void CompandingIsCorrectImpl(float e, float c, float expanded, float compressed)