Browse Source

Make ColorSpaceConverter immutable.

af/merge-core
James Jackson-South 8 years ago
parent
commit
0a5c84adbd
  1. 37
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs
  2. 8
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
  3. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
  4. 7
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
  5. 8
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
  6. 10
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
  7. 6
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
  8. 6
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
  9. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
  10. 98
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs
  11. 55
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs
  12. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzAndLmsConverter.cs
  13. 2
      tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs
  14. 4
      tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs
  15. 6
      tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs
  16. 6
      tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs
  17. 9
      tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs
  18. 20
      tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs
  19. 12
      tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs

37
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
@ -14,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <summary> /// <summary>
/// Performs chromatic adaptation of given <see cref="CieXyz"/> color. /// Performs chromatic adaptation of given <see cref="CieXyz"/> color.
/// Target white point is <see cref="WhitePoint"/>. /// Target white point is <see cref="ColorSpaceConverterOptions.WhitePoint"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <param name="sourceWhitePoint">The white point to adapt for</param> /// <param name="sourceWhitePoint">The white point to adapt for</param>
@ -22,11 +21,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint) public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint)
{ {
this.CheckChromaticAdaptation(); this.CheckChromaticAdaptation();
return this.ChromaticAdaptation.Transform(color, sourceWhitePoint, this.WhitePoint); return this.chromaticAdaptation.Transform(color, sourceWhitePoint, this.whitePoint);
} }
/// <summary> /// <summary>
/// Adapts <see cref="CieLab"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>. /// Adapts <see cref="CieLab"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetLabWhitePoint"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
@ -34,7 +33,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
this.CheckChromaticAdaptation(); this.CheckChromaticAdaptation();
if (color.WhitePoint.Equals(this.TargetLabWhitePoint)) if (color.WhitePoint.Equals(this.targetLabWhitePoint))
{ {
return color; return color;
} }
@ -44,7 +43,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
/// <summary> /// <summary>
/// Adapts <see cref="CieLch"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>. /// Adapts <see cref="CieLch"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetLabWhitePoint"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
@ -52,7 +51,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
this.CheckChromaticAdaptation(); this.CheckChromaticAdaptation();
if (color.WhitePoint.Equals(this.TargetLabWhitePoint)) if (color.WhitePoint.Equals(this.targetLabWhitePoint))
{ {
return color; return color;
} }
@ -62,7 +61,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
/// <summary> /// <summary>
/// Adapts <see cref="CieLchuv"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>. /// Adapts <see cref="CieLchuv"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetLabWhitePoint"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
@ -70,7 +69,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
this.CheckChromaticAdaptation(); this.CheckChromaticAdaptation();
if (color.WhitePoint.Equals(this.TargetLabWhitePoint)) if (color.WhitePoint.Equals(this.targetLabWhitePoint))
{ {
return color; return color;
} }
@ -80,7 +79,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
/// <summary> /// <summary>
/// Adapts <see cref="CieLuv"/> color from the source white point to white point set in <see cref="TargetLuvWhitePoint"/>. /// Adapts <see cref="CieLuv"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetLuvWhitePoint"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
@ -88,7 +87,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
this.CheckChromaticAdaptation(); this.CheckChromaticAdaptation();
if (color.WhitePoint.Equals(this.TargetLuvWhitePoint)) if (color.WhitePoint.Equals(this.targetLuvWhitePoint))
{ {
return color; return color;
} }
@ -98,7 +97,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
/// <summary> /// <summary>
/// Adapts <see cref="HunterLab"/> color from the source white point to white point set in <see cref="TargetHunterLabWhitePoint"/>. /// Adapts <see cref="HunterLab"/> color from the source white point to white point set in <see cref="ColorSpaceConverterOptions.TargetHunterLabWhitePoint"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
@ -106,7 +105,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
this.CheckChromaticAdaptation(); this.CheckChromaticAdaptation();
if (color.WhitePoint.Equals(this.TargetHunterLabWhitePoint)) if (color.WhitePoint.Equals(this.targetHunterLabWhitePoint))
{ {
return color; return color;
} }
@ -116,7 +115,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
} }
/// <summary> /// <summary>
/// Adapts a <see cref="LinearRgb"/> color from the source working space to working space set in <see cref="TargetRgbWorkingSpace"/>. /// Adapts a <see cref="LinearRgb"/> color from the source working space to working space set in <see cref="ColorSpaceConverterOptions.TargetRgbWorkingSpace"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
@ -124,7 +123,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
this.CheckChromaticAdaptation(); this.CheckChromaticAdaptation();
if (color.WorkingSpace.Equals(this.TargetRgbWorkingSpace)) if (color.WorkingSpace.Equals(this.targetRgbWorkingSpace))
{ {
return color; return color;
} }
@ -134,15 +133,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = converterToXYZ.Convert(color); CieXyz unadapted = converterToXYZ.Convert(color);
// Adaptation // Adaptation
CieXyz adapted = this.ChromaticAdaptation.Transform(unadapted, color.WorkingSpace.WhitePoint, this.TargetRgbWorkingSpace.WhitePoint); CieXyz adapted = this.chromaticAdaptation.Transform(unadapted, color.WorkingSpace.WhitePoint, this.targetRgbWorkingSpace.WhitePoint);
// Conversion back to RGB // Conversion back to RGB
CieXyzToLinearRgbConverter converterToRGB = this.GetCieXyxToLinearRgbConverter(this.TargetRgbWorkingSpace); CieXyzToLinearRgbConverter converterToRGB = this.GetCieXyxToLinearRgbConverter(this.targetRgbWorkingSpace);
return converterToRGB.Convert(adapted); return converterToRGB.Convert(adapted);
} }
/// <summary> /// <summary>
/// Adapts an <see cref="Rgb"/> color from the source working space to working space set in <see cref="TargetRgbWorkingSpace"/>. /// Adapts an <see cref="Rgb"/> color from the source working space to working space set in <see cref="ColorSpaceConverterOptions.TargetRgbWorkingSpace"/>.
/// </summary> /// </summary>
/// <param name="color">The color to adapt</param> /// <param name="color">The color to adapt</param>
/// <returns>The adapted color</returns> /// <returns>The adapted color</returns>
@ -157,7 +156,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
const string NoAdapterMessage = "Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."; const string NoAdapterMessage = "Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point.";
if (!this.IsChromaticAdaptationPerformed) if (!this.performChromaticAdaptation)
{ {
throw new InvalidOperationException(NoAdapterMessage); throw new InvalidOperationException(NoAdapterMessage);
} }

8
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
// Conversion (perserving white point) // Conversion (perserving white point)
CieLab unadapted = CieLchToCieLabConverter.Convert(color); CieLab unadapted = CieLchToCieLabConverter.Convert(color);
if (!this.IsChromaticAdaptationPerformed) if (!this.performChromaticAdaptation)
{ {
return unadapted; return unadapted;
} }
@ -165,12 +165,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieLab ToCieLab(in CieXyz color) public CieLab ToCieLab(in CieXyz color)
{ {
// Adaptation // Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed CieXyz adapted = !this.whitePoint.Equals(this.targetLabWhitePoint) && this.performChromaticAdaptation
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint) ? this.chromaticAdaptation.Transform(color, this.whitePoint, this.targetLabWhitePoint)
: color; : color;
// Conversion // Conversion
var converter = new CieXyzToCieLabConverter(this.TargetLabWhitePoint); var converter = new CieXyzToCieLabConverter(this.targetLabWhitePoint);
return converter.Convert(adapted); return converter.Convert(adapted);
} }

2
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs

@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieLch ToCieLch(in CieLab color) public CieLch ToCieLch(in CieLab color)
{ {
// Adaptation // Adaptation
CieLab adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color; CieLab adapted = this.performChromaticAdaptation ? this.Adapt(color) : color;
// Conversion // Conversion
return CieLabToCieLchConverter.Convert(adapted); return CieLabToCieLchConverter.Convert(adapted);

7
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs

@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieLchuv ToCieLchuv(in CieLuv color) public CieLchuv ToCieLchuv(in CieLuv color)
{ {
// Adaptation // Adaptation
CieLuv adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color; CieLuv adapted = this.performChromaticAdaptation ? this.Adapt(color) : color;
// Conversion // Conversion
return CieLuvToCieLchuvConverter.Convert(adapted); return CieLuvToCieLchuvConverter.Convert(adapted);
@ -388,7 +388,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(Rgb color) public CieLchuv ToCieLchuv(in Rgb color)
{ {
var xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
@ -423,8 +423,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns> /// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in YCbCr color) public CieLchuv ToCieLchuv(in YCbCr color)
{ {
CieXyz xyzColor = this.ToCieXyz(color); var xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor); return this.ToCieLchuv(xyzColor);
} }

8
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs

@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
// Conversion (perserving white point) // Conversion (perserving white point)
CieLuv unadapted = CieLchuvToCieLuvConverter.Convert(color); CieLuv unadapted = CieLchuvToCieLuvConverter.Convert(color);
if (!this.IsChromaticAdaptationPerformed) if (!this.performChromaticAdaptation)
{ {
return unadapted; return unadapted;
} }
@ -160,12 +160,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieLuv ToCieLuv(in CieXyz color) public CieLuv ToCieLuv(in CieXyz color)
{ {
// Adaptation // Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed CieXyz adapted = !this.whitePoint.Equals(this.targetLabWhitePoint) && this.performChromaticAdaptation
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint) ? this.chromaticAdaptation.Transform(color, this.whitePoint, this.targetLabWhitePoint)
: color; : color;
// Conversion // Conversion
var converter = new CieXyzToCieLuvConverter(this.TargetLuvWhitePoint); var converter = new CieXyzToCieLuvConverter(this.targetLuvWhitePoint);
return converter.Convert(adapted); return converter.Convert(adapted);
} }

10
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = CieLabToCieXyzConverter.Convert(color); CieXyz unadapted = CieLabToCieXyzConverter.Convert(color);
// Adaptation // Adaptation
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed CieXyz adapted = color.WhitePoint.Equals(this.whitePoint) || !this.performChromaticAdaptation
? unadapted ? unadapted
: this.Adapt(unadapted, color.WhitePoint); : this.Adapt(unadapted, color.WhitePoint);
@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = CieLuvToCieXyzConverter.Convert(color); CieXyz unadapted = CieLuvToCieXyzConverter.Convert(color);
// Adaptation // Adaptation
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed CieXyz adapted = color.WhitePoint.Equals(this.whitePoint) || !this.performChromaticAdaptation
? unadapted ? unadapted
: this.Adapt(unadapted, color.WhitePoint); : this.Adapt(unadapted, color.WhitePoint);
@ -314,7 +314,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color); CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color);
// Adaptation // Adaptation
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed CieXyz adapted = color.WhitePoint.Equals(this.whitePoint) || !this.performChromaticAdaptation
? unadapted ? unadapted
: this.Adapt(unadapted, color.WhitePoint); : this.Adapt(unadapted, color.WhitePoint);
@ -354,7 +354,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
CieXyz unadapted = converter.Convert(color); CieXyz unadapted = converter.Convert(color);
// Adaptation // Adaptation
return color.WorkingSpace.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed return color.WorkingSpace.WhitePoint.Equals(this.whitePoint) || !this.performChromaticAdaptation
? unadapted ? unadapted
: this.Adapt(unadapted, color.WorkingSpace.WhitePoint); : this.Adapt(unadapted, color.WorkingSpace.WhitePoint);
} }
@ -388,7 +388,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieXyz ToCieXyz(in Lms color) public CieXyz ToCieXyz(in Lms color)
{ {
// Conversion // Conversion
return this.cachedCieXyzAndLmsConverter.Convert(color); return this.cieXyzAndLmsConverter.Convert(color);
} }
/// <summary> /// <summary>

6
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs

@ -181,12 +181,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public HunterLab ToHunterLab(in CieXyz color) public HunterLab ToHunterLab(in CieXyz color)
{ {
// Adaptation // Adaptation
CieXyz adapted = !this.WhitePoint.Equals(this.TargetHunterLabWhitePoint) && this.IsChromaticAdaptationPerformed CieXyz adapted = !this.whitePoint.Equals(this.targetHunterLabWhitePoint) && this.performChromaticAdaptation
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetHunterLabWhitePoint) ? this.chromaticAdaptation.Transform(color, this.whitePoint, this.targetHunterLabWhitePoint)
: color; : color;
// Conversion // Conversion
return new CieXyzToHunterLabConverter(this.TargetHunterLabWhitePoint).Convert(adapted); return new CieXyzToHunterLabConverter(this.targetHunterLabWhitePoint).Convert(adapted);
} }
/// <summary> /// <summary>

6
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs

@ -185,12 +185,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public LinearRgb ToLinearRgb(in CieXyz color) public LinearRgb ToLinearRgb(in CieXyz color)
{ {
// Adaptation // Adaptation
CieXyz adapted = this.TargetRgbWorkingSpace.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed CieXyz adapted = this.targetRgbWorkingSpace.WhitePoint.Equals(this.whitePoint) || !this.performChromaticAdaptation
? color ? color
: this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetRgbWorkingSpace.WhitePoint); : this.chromaticAdaptation.Transform(color, this.whitePoint, this.targetRgbWorkingSpace.WhitePoint);
// Conversion // Conversion
CieXyzToLinearRgbConverter xyzConverter = this.GetCieXyxToLinearRgbConverter(this.TargetRgbWorkingSpace); CieXyzToLinearRgbConverter xyzConverter = this.GetCieXyxToLinearRgbConverter(this.targetRgbWorkingSpace);
return xyzConverter.Convert(adapted); return xyzConverter.Convert(adapted);
} }

2
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs

@ -179,7 +179,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns> /// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieXyz color) public Lms ToLms(in CieXyz color)
{ {
return this.cachedCieXyzAndLmsConverter.Convert(color); return this.cieXyzAndLmsConverter.Convert(color);
} }
/// <summary> /// <summary>

98
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs

@ -7,94 +7,46 @@ using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
/// <summary> /// <summary>
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. /// Provides methods to allow the conversion of color values into different color spaces.
/// </summary> /// </summary>
public partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
/// <summary> // Options.
/// The default whitepoint used for converting to CieLab private CieXyz whitePoint;
/// </summary> private CieXyz targetLuvWhitePoint;
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; private CieXyz targetLabWhitePoint;
private CieXyz targetHunterLabWhitePoint;
private RgbWorkingSpace targetRgbWorkingSpace;
private IChromaticAdaptation chromaticAdaptation;
private bool performChromaticAdaptation;
private Matrix4x4 lmsAdaptationMatrix;
private Matrix4x4 transformationMatrix; private CieXyzAndLmsConverter cieXyzAndLmsConverter;
private CieXyzAndLmsConverter cachedCieXyzAndLmsConverter;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ColorSpaceConverter"/> class. /// Initializes a new instance of the <see cref="ColorSpaceConverter"/> class.
/// </summary> /// </summary>
public ColorSpaceConverter() public ColorSpaceConverter()
: this(new ColorSpaceConverterOptions())
{ {
// Note the order here this is important.
this.WhitePoint = DefaultWhitePoint;
this.LmsAdaptationMatrix = CieXyzAndLmsConverter.DefaultTransformationMatrix;
this.ChromaticAdaptation = new VonKriesChromaticAdaptation(this.cachedCieXyzAndLmsConverter);
this.TargetLuvWhitePoint = CieLuv.DefaultWhitePoint;
this.TargetLabWhitePoint = CieLab.DefaultWhitePoint;
this.TargetHunterLabWhitePoint = HunterLab.DefaultWhitePoint;
this.TargetRgbWorkingSpace = Rgb.DefaultWorkingSpace;
} }
/// <summary> /// <summary>
/// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space. /// Initializes a new instance of the <see cref="ColorSpaceConverter"/> class.
/// When null, no adaptation will be performed.
/// </summary>
public CieXyz WhitePoint { get; set; }
/// <summary>
/// Gets or sets the white point used *when creating* Luv/LChuv colors. (Luv/LChuv colors on the input already contain the white point information)
/// Defaults to: <see cref="CieLuv.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetLuvWhitePoint { get; set; }
/// <summary>
/// Gets or sets the white point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information)
/// Defaults to: <see cref="CieLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetLabWhitePoint { get; set; }
/// <summary>
/// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information)
/// Defaults to: <see cref="HunterLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetHunterLabWhitePoint { get; set; }
/// <summary>
/// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information)
/// Defaults to: <see cref="Rgb.DefaultWorkingSpace"/>.
/// </summary>
public RgbWorkingSpace TargetRgbWorkingSpace { get; set; }
/// <summary>
/// Gets or sets the chromatic adaptation method used. When null, no adaptation will be performed.
/// </summary>
public IChromaticAdaptation ChromaticAdaptation { get; set; }
/// <summary>
/// Gets or sets transformation matrix used in conversion to <see cref="Lms"/>,
/// also used in the default Von Kries Chromatic Adaptation method.
/// </summary> /// </summary>
public Matrix4x4 LmsAdaptationMatrix /// <param name="options">The configuration options.</param>
public ColorSpaceConverter(ColorSpaceConverterOptions options)
{ {
get => this.transformationMatrix; Guard.NotNull(options, nameof(options));
this.whitePoint = options.WhitePoint;
set this.targetLuvWhitePoint = options.TargetLuvWhitePoint;
{ this.targetLabWhitePoint = options.TargetLabWhitePoint;
this.transformationMatrix = value; this.targetHunterLabWhitePoint = options.TargetHunterLabWhitePoint;
if (this.cachedCieXyzAndLmsConverter == null) this.targetRgbWorkingSpace = options.TargetRgbWorkingSpace;
{ this.chromaticAdaptation = options.ChromaticAdaptation;
this.cachedCieXyzAndLmsConverter = new CieXyzAndLmsConverter(value); this.performChromaticAdaptation = this.chromaticAdaptation != null;
} this.lmsAdaptationMatrix = options.LmsAdaptationMatrix;
else this.cieXyzAndLmsConverter = new CieXyzAndLmsConverter(this.lmsAdaptationMatrix);
{
this.cachedCieXyzAndLmsConverter.TransformationMatrix = value;
}
}
} }
/// <summary>
/// Gets a value indicating whether chromatic adaptation has been performed.
/// </summary>
private bool IsChromaticAdaptationPerformed => this.ChromaticAdaptation != null;
} }
} }

55
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs

@ -0,0 +1,55 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
/// <summary>
/// Configuration options for the <see cref="ColorSpaceConverter"/> class.
/// </summary>
public class ColorSpaceConverterOptions
{
/// <summary>
/// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space.
/// When <value>default</value>, no adaptation will be performed.
/// Defaults to: <see cref="CieLuv.DefaultWhitePoint"/>.
/// </summary>
public CieXyz WhitePoint { get; set; } = CieLuv.DefaultWhitePoint;
/// <summary>
/// Gets or sets the white point used *when creating* Luv/LChuv colors. (Luv/LChuv colors on the input already contain the white point information)
/// Defaults to: <see cref="CieLuv.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetLuvWhitePoint { get; set; } = CieLuv.DefaultWhitePoint;
/// <summary>
/// Gets or sets the white point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information)
/// Defaults to: <see cref="CieLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetLabWhitePoint { get; set; } = CieLab.DefaultWhitePoint;
/// <summary>
/// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information)
/// Defaults to: <see cref="HunterLab.DefaultWhitePoint"/>.
/// </summary>
public CieXyz TargetHunterLabWhitePoint { get; set; } = HunterLab.DefaultWhitePoint;
/// <summary>
/// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information)
/// Defaults to: <see cref="Rgb.DefaultWorkingSpace"/>.
/// </summary>
public RgbWorkingSpace TargetRgbWorkingSpace { get; set; } = Rgb.DefaultWorkingSpace;
/// <summary>
/// Gets or sets the chromatic adaptation method used. When <value>null</value>, no adaptation will be performed.
/// </summary>
public IChromaticAdaptation ChromaticAdaptation { get; set; } = new VonKriesChromaticAdaptation();
/// <summary>
/// Gets or sets transformation matrix used in conversion to and from <see cref="Lms"/>.
/// </summary>
public Matrix4x4 LmsAdaptationMatrix { get; set; } = CieXyzAndLmsConverter.DefaultTransformationMatrix;
}
}

20
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzAndLmsConverter.cs

@ -39,24 +39,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix) public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix)
{ {
this.TransformationMatrix = transformationMatrix; this.transformationMatrix = transformationMatrix;
} Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix);
/// <summary>
/// Gets or sets the transformation matrix used for the conversion (definition of the cone response domain).
/// <see cref="LmsAdaptationMatrix"/>
/// </summary>
public Matrix4x4 TransformationMatrix
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.transformationMatrix;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.transformationMatrix = value;
Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix);
}
} }
/// <inheritdoc/> /// <inheritdoc/>

2
tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs

@ -14,7 +14,7 @@
private static readonly RGBColor RGBColor = new RGBColor(0.206162, 0.260277, 0.746717, RGBWorkingSpaces.WideGamutRGB); private static readonly RGBColor RGBColor = new RGBColor(0.206162, 0.260277, 0.746717, RGBWorkingSpaces.WideGamutRGB);
private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb });
private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter { TargetRGBWorkingSpace = RGBWorkingSpaces.sRGB }; private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter { TargetRGBWorkingSpace = RGBWorkingSpaces.sRGB };

4
tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs

@ -17,6 +17,10 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces
{ {
var x = default(CieLab); var x = default(CieLab);
var y = new CieLab(Vector3.One); var y = new CieLab(Vector3.One);
Assert.True(default(CieLab) == default(CieLab));
Assert.True(default(CieLab) != new CieLab(1, 0, 1));
Assert.False(default(CieLab) == new CieLab(1, 0, 1));
Assert.Equal(default(CieLab), default(CieLab)); Assert.Equal(default(CieLab), default(CieLab));
Assert.Equal(new CieLab(1, 0, 1), new CieLab(1, 0, 1)); Assert.Equal(new CieLab(1, 0, 1), new CieLab(1, 0, 1));
Assert.Equal(new CieLab(Vector3.One), new CieLab(Vector3.One)); Assert.Equal(new CieLab(Vector3.One), new CieLab(Vector3.One));

6
tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs

@ -35,7 +35,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
{ {
// Arrange // Arrange
var input = new CieLab(l, a, b, Illuminants.D65); var input = new CieLab(l, a, b, Illuminants.D65);
var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 };
var converter = new ColorSpaceConverter(options);
var expected = new CieXyz(x, y, z); var expected = new CieXyz(x, y, z);
Span<CieLab> inputSpan = new CieLab[5]; Span<CieLab> inputSpan = new CieLab[5];
@ -70,7 +71,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
{ {
// Arrange // Arrange
var input = new CieXyz(x, y, z); var input = new CieXyz(x, y, z);
var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 };
var converter = new ColorSpaceConverter(options);
var expected = new CieLab(l, a, b); var expected = new CieLab(l, a, b);
Span<CieXyz> inputSpan = new CieXyz[5]; Span<CieXyz> inputSpan = new CieXyz[5];

6
tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs

@ -34,7 +34,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
{ {
// Arrange // Arrange
var input = new CieLuv(l, u, v, Illuminants.D65); var input = new CieLuv(l, u, v, Illuminants.D65);
var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 };
var converter = new ColorSpaceConverter(options);
var expected = new CieXyz(x, y, z); var expected = new CieXyz(x, y, z);
Span<CieLuv> inputSpan = new CieLuv[5]; Span<CieLuv> inputSpan = new CieLuv[5];
@ -69,7 +70,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
{ {
// Arrange // Arrange
var input = new CieXyz(x, y, z); var input = new CieXyz(x, y, z);
var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 };
var converter = new ColorSpaceConverter(options);
var expected = new CieLuv(l, u, v); var expected = new CieLuv(l, u, v);
Span<CieXyz> inputSpan = new CieXyz[5]; Span<CieXyz> inputSpan = new CieXyz[5];

9
tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs

@ -29,7 +29,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
{ {
// Arrange // Arrange
var input = new HunterLab(l, a, b); var input = new HunterLab(l, a, b);
var converter = new ColorSpaceConverter { WhitePoint = Illuminants.C }; var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.C };
var converter = new ColorSpaceConverter(options);
var expected = new CieXyz(x, y, z); var expected = new CieXyz(x, y, z);
Span<HunterLab> inputSpan = new HunterLab[5]; Span<HunterLab> inputSpan = new HunterLab[5];
@ -60,7 +61,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
{ {
// Arrange // Arrange
var input = new HunterLab(l, a, b); var input = new HunterLab(l, a, b);
var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65 }; var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65 };
var converter = new ColorSpaceConverter(options);
var expected = new CieXyz(x, y, z); var expected = new CieXyz(x, y, z);
Span<HunterLab> inputSpan = new HunterLab[5]; Span<HunterLab> inputSpan = new HunterLab[5];
@ -91,7 +93,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
{ {
// Arrange // Arrange
var input = new CieXyz(x, y, z); var input = new CieXyz(x, y, z);
var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65 }; var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65 };
var converter = new ColorSpaceConverter(options);
var expected = new HunterLab(l, a, b); var expected = new HunterLab(l, a, b);
Span<CieXyz> inputSpan = new CieXyz[5]; Span<CieXyz> inputSpan = new CieXyz[5];

20
tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs

@ -27,7 +27,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
// Arrange // Arrange
var input = new Rgb(r1, g1, b1, RgbWorkingSpaces.WideGamutRgb); var input = new Rgb(r1, g1, b1, RgbWorkingSpaces.WideGamutRgb);
var expected = new Rgb(r2, g2, b2, RgbWorkingSpaces.SRgb); var expected = new Rgb(r2, g2, b2, RgbWorkingSpaces.SRgb);
var converter = new ColorSpaceConverter { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; var options = new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb };
var converter = new ColorSpaceConverter(options);
// Action // Action
Rgb actual = converter.Adapt(input); Rgb actual = converter.Adapt(input);
@ -46,7 +47,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
// Arrange // Arrange
var input = new Rgb(r1, g1, b1, RgbWorkingSpaces.SRgb); var input = new Rgb(r1, g1, b1, RgbWorkingSpaces.SRgb);
var expected = new Rgb(r2, g2, b2, RgbWorkingSpaces.WideGamutRgb); var expected = new Rgb(r2, g2, b2, RgbWorkingSpaces.WideGamutRgb);
var converter = new ColorSpaceConverter { TargetRgbWorkingSpace = RgbWorkingSpaces.WideGamutRgb }; var options = new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.WideGamutRgb };
var converter = new ColorSpaceConverter(options);
// Action // Action
Rgb actual = converter.Adapt(input); Rgb actual = converter.Adapt(input);
@ -64,7 +66,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
// Arrange // Arrange
var input = new CieLab(l1, a1, b1, Illuminants.D65); var input = new CieLab(l1, a1, b1, Illuminants.D65);
var expected = new CieLab(l2, a2, b2); var expected = new CieLab(l2, a2, b2);
var converter = new ColorSpaceConverter { TargetLabWhitePoint = Illuminants.D50 }; var options = new ColorSpaceConverterOptions { TargetLabWhitePoint = Illuminants.D50 };
var converter = new ColorSpaceConverter(options);
// Action // Action
CieLab actual = converter.Adapt(input); CieLab actual = converter.Adapt(input);
@ -81,7 +84,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
// Arrange // Arrange
var input = new CieXyz(x1, y1, z1); var input = new CieXyz(x1, y1, z1);
var expected = new CieXyz(x2, y2, z2); var expected = new CieXyz(x2, y2, z2);
var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D50 }; var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D50 };
var converter = new ColorSpaceConverter(options);
// Action // Action
CieXyz actual = converter.Adapt(input, Illuminants.D65); CieXyz actual = converter.Adapt(input, Illuminants.D65);
@ -98,12 +102,14 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
// Arrange // Arrange
var input = new CieXyz(x1, y1, z1); var input = new CieXyz(x1, y1, z1);
var expected = new CieXyz(x2, y2, z2); var expected = new CieXyz(x2, y2, z2);
var converter = new ColorSpaceConverter var options = new ColorSpaceConverterOptions
{ {
ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling),
WhitePoint = Illuminants.D50 WhitePoint = Illuminants.D50
}; };
var converter = new ColorSpaceConverter(options);
// Action // Action
CieXyz actual = converter.Adapt(input, Illuminants.D65); CieXyz actual = converter.Adapt(input, Illuminants.D65);
@ -119,12 +125,14 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
// Arrange // Arrange
var input = new CieXyz(x1, y1, z1); var input = new CieXyz(x1, y1, z1);
var expected = new CieXyz(x2, y2, z2); var expected = new CieXyz(x2, y2, z2);
var converter = new ColorSpaceConverter var options = new ColorSpaceConverterOptions
{ {
ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling),
WhitePoint = Illuminants.D50 WhitePoint = Illuminants.D50
}; };
var converter = new ColorSpaceConverter(options);
// Action // Action
CieXyz actual = converter.Adapt(input, Illuminants.D65); CieXyz actual = converter.Adapt(input, Illuminants.D65);

12
tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs

@ -34,7 +34,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
{ {
// Arrange // Arrange
var input = new CieXyz(x, y, z); var input = new CieXyz(x, y, z);
var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb };
var converter = new ColorSpaceConverter(options);
var expected = new Rgb(r, g, b); var expected = new Rgb(r, g, b);
Span<CieXyz> inputSpan = new CieXyz[5]; Span<CieXyz> inputSpan = new CieXyz[5];
@ -72,7 +73,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
{ {
// Arrange // Arrange
var input = new CieXyz(x, y, z); var input = new CieXyz(x, y, z);
var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb };
var converter = new ColorSpaceConverter(options);
var expected = new Rgb(r, g, b); var expected = new Rgb(r, g, b);
Span<CieXyz> inputSpan = new CieXyz[5]; Span<CieXyz> inputSpan = new CieXyz[5];
@ -109,7 +111,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
{ {
// Arrange // Arrange
var input = new Rgb(r, g, b); var input = new Rgb(r, g, b);
var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D50 }; var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D50 };
var converter = new ColorSpaceConverter(options);
var expected = new CieXyz(x, y, z); var expected = new CieXyz(x, y, z);
Span<Rgb> inputSpan = new Rgb[5]; Span<Rgb> inputSpan = new Rgb[5];
@ -145,7 +148,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion
{ {
// Arrange // Arrange
var input = new Rgb(r, g, b); var input = new Rgb(r, g, b);
var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65 }; var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65 };
var converter = new ColorSpaceConverter(options);
var expected = new CieXyz(x, y, z); var expected = new CieXyz(x, y, z);
Span<Rgb> inputSpan = new Rgb[5]; Span<Rgb> inputSpan = new Rgb[5];

Loading…
Cancel
Save