Skip to content

Commit 9fdf204

Browse files
Complete conversion tests
1 parent 183a821 commit 9fdf204

20 files changed

+745
-49
lines changed

src/ImageSharp/ColorProfiles/CieLuv.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,6 @@ public CieLuv(float l, float u, float v)
4646
/// </summary>
4747
public readonly float V { get; }
4848

49-
/// <summary>
50-
/// Gets the reference white point of this color
51-
/// </summary>
52-
public readonly CieXyz WhitePoint { get; }
53-
5449
/// <summary>
5550
/// Compares two <see cref="CieLuv"/> objects for equality.
5651
/// </summary>
@@ -185,7 +180,7 @@ public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSo
185180
=> ChromaticAdaptionWhitePointSource.WhitePoint;
186181

187182
/// <inheritdoc/>
188-
public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V, this.WhitePoint);
183+
public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V);
189184

190185
/// <inheritdoc/>
191186
public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})");
@@ -198,8 +193,7 @@ public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSo
198193
public bool Equals(CieLuv other)
199194
=> this.L.Equals(other.L)
200195
&& this.U.Equals(other.U)
201-
&& this.V.Equals(other.V)
202-
&& this.WhitePoint.Equals(other.WhitePoint);
196+
&& this.V.Equals(other.V);
203197

204198
[MethodImpl(MethodImplOptions.AggressiveInlining)]
205199
private static double ComputeU(in CieXyz source)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Buffers;
5+
using SixLabors.ImageSharp.Memory;
6+
7+
namespace SixLabors.ImageSharp.ColorProfiles;
8+
9+
internal static class ColorProfileConverterExtensionsCieLabRgb
10+
{
11+
public static TTo Convert<TFrom, TTo>(this ColorProfileConverter converter, TFrom source)
12+
where TFrom : struct, IColorProfile<TFrom, CieLab>
13+
where TTo : struct, IColorProfile<TTo, Rgb>
14+
{
15+
ColorConversionOptions options = converter.Options;
16+
17+
// Convert to input PCS
18+
CieLab pcsFromA = source.ToProfileConnectingSpace(options);
19+
CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options);
20+
21+
// Adapt to target white point
22+
pcsFromB = VonKriesChromaticAdaptation.Transform<TFrom, TTo>(options, in pcsFromB);
23+
24+
// Convert between PCS
25+
Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB);
26+
27+
// Convert to output from PCS
28+
return TTo.FromProfileConnectingSpace(options, pcsTo);
29+
}
30+
31+
public static void Convert<TFrom, TTo>(this ColorProfileConverter converter, ReadOnlySpan<TFrom> source, Span<TTo> destination)
32+
where TFrom : struct, IColorProfile<TFrom, CieLab>
33+
where TTo : struct, IColorProfile<TTo, Rgb>
34+
{
35+
ColorConversionOptions options = converter.Options;
36+
37+
// Convert to input PCS.
38+
using IMemoryOwner<CieLab> pcsFromAOwner = options.MemoryAllocator.Allocate<CieLab>(source.Length);
39+
Span<CieLab> pcsFromA = pcsFromAOwner.GetSpan();
40+
TFrom.ToProfileConnectionSpace(options, source, pcsFromA);
41+
42+
using IMemoryOwner<CieXyz> pcsFromBOwner = options.MemoryAllocator.Allocate<CieXyz>(source.Length);
43+
Span<CieXyz> pcsFromB = pcsFromBOwner.GetSpan();
44+
CieLab.ToProfileConnectionSpace(options, pcsFromA, pcsFromB);
45+
46+
// Adapt to target white point
47+
VonKriesChromaticAdaptation.Transform<TFrom, TTo>(options, pcsFromB, pcsFromB);
48+
49+
// Convert between PCS.
50+
using IMemoryOwner<Rgb> pcsToOwner = options.MemoryAllocator.Allocate<Rgb>(source.Length);
51+
Span<Rgb> pcsTo = pcsToOwner.GetSpan();
52+
Rgb.FromProfileConnectionSpace(options, pcsFromB, pcsTo);
53+
54+
// Convert to output from PCS
55+
TTo.FromProfileConnectionSpace(options, pcsTo, destination);
56+
}
57+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Buffers;
5+
using SixLabors.ImageSharp.Memory;
6+
7+
namespace SixLabors.ImageSharp.ColorProfiles;
8+
9+
internal static class ColorProfileConverterExtensionsRgbCieLab
10+
{
11+
public static TTo Convert<TFrom, TTo>(this ColorProfileConverter converter, TFrom source)
12+
where TFrom : struct, IColorProfile<TFrom, Rgb>
13+
where TTo : struct, IColorProfile<TTo, CieLab>
14+
{
15+
ColorConversionOptions options = converter.Options;
16+
17+
// Convert to input PCS
18+
Rgb pcsFromA = source.ToProfileConnectingSpace(options);
19+
CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options);
20+
21+
// Adapt to target white point
22+
pcsFromB = VonKriesChromaticAdaptation.Transform<TFrom, TTo>(options, in pcsFromB);
23+
24+
// Convert between PCS
25+
CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB);
26+
27+
// Convert to output from PCS
28+
return TTo.FromProfileConnectingSpace(options, pcsTo);
29+
}
30+
31+
public static void Convert<TFrom, TTo>(this ColorProfileConverter converter, ReadOnlySpan<TFrom> source, Span<TTo> destination)
32+
where TFrom : struct, IColorProfile<TFrom, Rgb>
33+
where TTo : struct, IColorProfile<TTo, CieLab>
34+
{
35+
ColorConversionOptions options = converter.Options;
36+
37+
// Convert to input PCS.
38+
using IMemoryOwner<Rgb> pcsFromAOwner = options.MemoryAllocator.Allocate<Rgb>(source.Length);
39+
Span<Rgb> pcsFromA = pcsFromAOwner.GetSpan();
40+
TFrom.ToProfileConnectionSpace(options, source, pcsFromA);
41+
42+
using IMemoryOwner<CieXyz> pcsFromBOwner = options.MemoryAllocator.Allocate<CieXyz>(source.Length);
43+
Span<CieXyz> pcsFromB = pcsFromBOwner.GetSpan();
44+
Rgb.ToProfileConnectionSpace(options, pcsFromA, pcsFromB);
45+
46+
// Adapt to target white point
47+
VonKriesChromaticAdaptation.Transform<TFrom, TTo>(options, pcsFromB, pcsFromB);
48+
49+
// Convert between PCS.
50+
using IMemoryOwner<CieLab> pcsToOwner = options.MemoryAllocator.Allocate<CieLab>(source.Length);
51+
Span<CieLab> pcsTo = pcsToOwner.GetSpan();
52+
CieLab.FromProfileConnectionSpace(options, pcsFromB, pcsTo);
53+
54+
// Convert to output from PCS
55+
TTo.FromProfileConnectionSpace(options, pcsTo, destination);
56+
}
57+
}

src/ImageSharp/ColorProfiles/Hsl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public static Hsl FromProfileConnectingSpace(ColorConversionOptions options, in
114114
}
115115

116116
h *= 60F;
117-
if (h < 0F)
117+
if (h < -Constants.Epsilon)
118118
{
119119
h += 360F;
120120
}

src/ImageSharp/ColorProfiles/Hsv.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,10 @@ public static Hsv FromProfileConnectingSpace(ColorConversionOptions options, in
111111
h = 4 + ((r - g) / chroma);
112112
}
113113

114-
h *= 60;
115-
if (h < 0f)
114+
h *= 60F;
115+
if (h < -Constants.Epsilon)
116116
{
117-
h += 360;
117+
h += 360F;
118118
}
119119

120120
s = chroma / v;

src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorProfiles;
1010
/// </summary>
1111
/// <remarks>
1212
/// Matrix data obtained from:
13-
/// Two New von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization
13+
/// Two New Von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization
1414
/// S. Bianco, R. Schettini
1515
/// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy
1616
/// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf

tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class CieXyyAndHsvConversionTests
1515
[Theory]
1616
[InlineData(0, 0, 0, 0, 0, 0)]
1717
[InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.42770)]
18-
public void Convert_CieXyy_to_Hsv(float x, float y, float yl, float h, float s, float v)
18+
public void Convert_CieXyy_To_Hsv(float x, float y, float yl, float h, float s, float v)
1919
{
2020
// Arrange
2121
CieXyy input = new(x, y, yl);
@@ -43,7 +43,7 @@ public void Convert_CieXyy_to_Hsv(float x, float y, float yl, float h, float s,
4343
[Theory]
4444
[InlineData(0, 0, 0, 0, 0, 0)]
4545
[InlineData(120, 1, 0.42770, 0.32114, 0.59787, 0.10976)]
46-
public void Convert_Hsv_to_CieXyy(float h, float s, float v, float x, float y, float yl)
46+
public void Convert_Hsv_To_CieXyy(float h, float s, float v, float x, float y, float yl)
4747
{
4848
// Arrange
4949
Hsv input = new(h, s, v);

tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class CieXyyAndHunterLabConversionTests
1515
[Theory]
1616
[InlineData(0, 0, 0, 0, 0, 0)]
1717
[InlineData(0.360555, 0.936901, 0.1001514, 31.6467056, -33.00599, 25.67032)]
18-
public void Convert_CieXyy_to_HunterLab(float x, float y, float yl, float l, float a, float b)
18+
public void Convert_CieXyy_To_HunterLab(float x, float y, float yl, float l, float a, float b)
1919
{
2020
// Arrange
2121
CieXyy input = new(x, y, yl);
@@ -43,7 +43,7 @@ public void Convert_CieXyy_to_HunterLab(float x, float y, float yl, float l, flo
4343
[Theory]
4444
[InlineData(0, 0, 0, 0, 0, 0)]
4545
[InlineData(31.6467056, -33.00599, 25.67032, 0.360555, 0.936901, 0.1001514)]
46-
public void Convert_HunterLab_to_CieXyy(float l, float a, float b, float x, float y, float yl)
46+
public void Convert_HunterLab_To_CieXyy(float l, float a, float b, float x, float y, float yl)
4747
{
4848
// Arrange
4949
HunterLab input = new(l, a, b);

tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,28 @@ public class CieXyzAndCieLuvConversionTest
1818

1919
[Theory]
2020
[InlineData(0, 0, 0, 0, 0, 0)]
21-
[InlineData(0, 100, 50, 0, 0, 0)]
22-
[InlineData(0.1, 100, 50, 0.000493, 0.000111, -0.000709)]
23-
[InlineData(70.0000, 86.3525, 2.8240, 0.569310, 0.407494, 0.365843)]
24-
[InlineData(10.0000, -1.2345, -10.0000, 0.012191, 0.011260, 0.025939)]
25-
[InlineData(100, 0, 0, 0.950470, 1.000000, 1.088830)]
26-
[InlineData(1, 1, 1, 0.001255, 0.001107, 0.000137)]
27-
public void Convert_Luv_to_Xyz(float l, float u, float v, float x, float y, float z)
21+
[InlineData(0.000493, 0.000111, 0, 0.10026589, 0.9332349, -0.00704865158)]
22+
[InlineData(0.569310, 0.407494, 0.365843, 70.0000, 86.3524, 2.8240)]
23+
[InlineData(0.012191, 0.011260, 0.025939, 9.9998, -1.2343, -9.9999)]
24+
[InlineData(0.950470, 1.000000, 1.088830, 100, 0, 0)]
25+
[InlineData(0.001255, 0.001107, 0.000137, 0.9999, 0.9998, 1.0004)]
26+
public void Convert_Xyz_To_Luv(float x, float y, float z, float l, float u, float v)
2827
{
2928
// Arrange
30-
CieLuv input = new(l, u, v);
31-
CieXyz expected = new(x, y, z);
29+
CieXyz input = new(x, y, z);
30+
CieLuv expected = new(l, u, v);
3231

3332
ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 };
3433
ColorProfileConverter converter = new(options);
3534

36-
Span<CieLuv> inputSpan = new CieLuv[5];
35+
Span<CieXyz> inputSpan = new CieXyz[5];
3736
inputSpan.Fill(input);
3837

39-
Span<CieXyz> actualSpan = new CieXyz[5];
38+
Span<CieLuv> actualSpan = new CieLuv[5];
4039

4140
// Act
42-
CieXyz actual = converter.Convert<CieLuv, CieXyz>(input);
43-
converter.Convert<CieLuv, CieXyz>(inputSpan, actualSpan);
41+
CieLuv actual = converter.Convert<CieXyz, CieLuv>(input);
42+
converter.Convert<CieXyz, CieLuv>(inputSpan, actualSpan);
4443

4544
// Assert
4645
Assert.Equal(expected, actual, Comparer);
@@ -53,27 +52,29 @@ public void Convert_Luv_to_Xyz(float l, float u, float v, float x, float y, floa
5352

5453
[Theory]
5554
[InlineData(0, 0, 0, 0, 0, 0)]
56-
[InlineData(0.000493, 0.000111, 0, 0.1003, 0.9332, -0.0070)]
57-
[InlineData(0.569310, 0.407494, 0.365843, 70.0000, 86.3524, 2.8240)]
58-
[InlineData(0.012191, 0.011260, 0.025939, 9.9998, -1.2343, -9.9999)]
59-
[InlineData(0.950470, 1.000000, 1.088830, 100, 0, 0)]
60-
[InlineData(0.001255, 0.001107, 0.000137, 0.9999, 0.9998, 1.0004)]
61-
public void Convert_Xyz_to_Luv(float x, float y, float z, float l, float u, float v)
55+
[InlineData(0, 100, 50, 0, 0, 0)]
56+
[InlineData(0.1, 100, 50, 0.000493, 0.000111, -0.000709)]
57+
[InlineData(70.0000, 86.3525, 2.8240, 0.569310, 0.407494, 0.365843)]
58+
[InlineData(10.0000, -1.2345, -10.0000, 0.012191, 0.011260, 0.025939)]
59+
[InlineData(100, 0, 0, 0.950470, 1.000000, 1.088830)]
60+
[InlineData(1, 1, 1, 0.001255, 0.001107, 0.000137)]
61+
public void Convert_Luv_To_Xyz(float l, float u, float v, float x, float y, float z)
6262
{
6363
// Arrange
64-
CieXyz input = new(x, y, z);
65-
CieLuv expected = new(l, u, v);
64+
CieLuv input = new(l, u, v);
65+
CieXyz expected = new(x, y, z);
6666

6767
ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 };
6868
ColorProfileConverter converter = new(options);
6969

70-
Span<CieXyz> inputSpan = new CieXyz[5];
70+
Span<CieLuv> inputSpan = new CieLuv[5];
7171
inputSpan.Fill(input);
7272

73-
Span<CieLuv> actualSpan = new CieLuv[5];
73+
Span<CieXyz> actualSpan = new CieXyz[5];
7474

7575
// Act
76-
CieLuv actual = converter.Convert<CieXyz, CieLuv>(input);
76+
CieXyz actual = converter.Convert<CieLuv, CieXyz>(input);
77+
converter.Convert<CieLuv, CieXyz>(inputSpan, actualSpan);
7778

7879
// Assert
7980
Assert.Equal(expected, actual, Comparer);
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using SixLabors.ImageSharp.ColorProfiles;
5+
6+
namespace SixLabors.ImageSharp.Tests.ColorProfiles;
7+
8+
/// <summary>
9+
/// Tests <see cref="CieXyz"/>-<see cref="Hsl"/> conversions.
10+
/// </summary>
11+
public class CieXyzAndHslConversionTests
12+
{
13+
private static readonly ApproximateColorProfileComparer Comparer = new(.0002F);
14+
15+
[Theory]
16+
[InlineData(0, 0, 0, 0, 0, 0)]
17+
[InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.5)]
18+
public void Convert_CieXyz_to_Hsl(float x, float y, float yl, float h, float s, float l)
19+
{
20+
// Arrange
21+
CieXyz input = new(x, y, yl);
22+
Hsl expected = new(h, s, l);
23+
ColorProfileConverter converter = new();
24+
25+
Span<CieXyz> inputSpan = new CieXyz[5];
26+
inputSpan.Fill(input);
27+
28+
Span<Hsl> actualSpan = new Hsl[5];
29+
30+
// Act
31+
Hsl actual = converter.Convert<CieXyz, Hsl>(input);
32+
converter.Convert<CieXyz, Hsl>(inputSpan, actualSpan);
33+
34+
// Assert
35+
Assert.Equal(expected, actual, Comparer);
36+
37+
for (int i = 0; i < actualSpan.Length; i++)
38+
{
39+
Assert.Equal(expected, actualSpan[i], Comparer);
40+
}
41+
}
42+
43+
[Theory]
44+
[InlineData(0, 0, 0, 0, 0, 0)]
45+
[InlineData(120, 1, 0.5, 0.38506496, 0.716878653, 0.09710451)]
46+
public void Convert_Hsl_to_CieXyz(float h, float s, float l, float x, float y, float yl)
47+
{
48+
// Arrange
49+
Hsl input = new(h, s, l);
50+
CieXyz expected = new(x, y, yl);
51+
ColorProfileConverter converter = new();
52+
53+
Span<Hsl> inputSpan = new Hsl[5];
54+
inputSpan.Fill(input);
55+
56+
Span<CieXyz> actualSpan = new CieXyz[5];
57+
58+
// Act
59+
CieXyz actual = converter.Convert<Hsl, CieXyz>(input);
60+
converter.Convert<Hsl, CieXyz>(inputSpan, actualSpan);
61+
62+
// Assert
63+
Assert.Equal(expected, actual, Comparer);
64+
65+
for (int i = 0; i < actualSpan.Length; i++)
66+
{
67+
Assert.Equal(expected, actualSpan[i], Comparer);
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)