Browse Source

Refactor and add tests

js/color-alpha-handling
Nicolas Portmann 5 years ago
parent
commit
328b37af4b
  1. 438
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs

438
tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs

@ -42,9 +42,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Theory]
[MemberData(nameof(CommonConversionData))]
public void ConvertFromYCbCrBasic(int inputBufferLength, int resultBufferLength, int seed)
public void FromYCbCrBasic(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateRgbToYCbCrConversion(
ValidateConversion(
new JpegColorConverter.FromYCbCrBasic(8),
3,
inputBufferLength,
@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[MemberData(nameof(CommonConversionData))]
public void FromYCbCrVector(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateRgbToYCbCrConversion(
ValidateConversion(
new JpegColorConverter.FromYCbCrVector(8),
3,
inputBufferLength,
@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
return;
}
ValidateRgbToYCbCrConversion(
ValidateConversion(
new JpegColorConverter.FromYCbCrVector8(8),
3,
inputBufferLength,
@ -92,8 +92,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
return;
}
// JpegColorConverter.FromYCbCrSimdAvx2.LogPlz = s => this.Output.WriteLine(s);
ValidateRgbToYCbCrConversion(
ValidateConversion(
new JpegColorConverter.FromYCbCrAvx2(8),
3,
inputBufferLength,
@ -103,7 +102,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Theory]
[MemberData(nameof(CommonConversionData))]
public void ConvertFromYCbCr_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
public void FromYCbCr_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
JpegColorSpace.YCbCr,
@ -113,149 +112,269 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
seed);
}
// Benchmark, for local execution only
// [Theory]
// [InlineData(false)]
// [InlineData(true)]
public void BenchmarkYCbCr(bool simd)
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromCmykBasic(int inputBufferLength, int resultBufferLength, int seed)
{
int count = 2053;
int times = 50000;
JpegColorConverter.ComponentValues values = CreateRandomValues(3, count, 1);
var result = new Vector4[count];
ValidateConversion(
new JpegColorConverter.FromCmykBasic(8),
4,
inputBufferLength,
resultBufferLength,
seed);
}
JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrVector(8) : new JpegColorConverter.FromYCbCrBasic(8);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromCmykVector8(int inputBufferLength, int resultBufferLength, int seed)
{
if (!SimdUtils.HasVector8)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
return;
}
// Warm up:
converter.ConvertToRgba(values, result);
ValidateConversion(
new JpegColorConverter.FromCmykVector8(8),
4,
inputBufferLength,
resultBufferLength,
seed);
}
using (new MeasureGuard(this.Output, $"{converter.GetType().Name} x {times}"))
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromCmykAvx2(int inputBufferLength, int resultBufferLength, int seed)
{
if (!SimdUtils.HasAvx2)
{
for (int i = 0; i < times; i++)
{
converter.ConvertToRgba(values, result);
}
this.Output.WriteLine("No AVX2 present, skipping test!");
return;
}
ValidateConversion(
new JpegColorConverter.FromCmykAvx2(8),
4,
inputBufferLength,
resultBufferLength,
seed);
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void ConvertFromCmyk(int inputBufferLength, int resultBufferLength, int seed)
public void FromCmyk_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
{
var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
ValidateConversion(
JpegColorSpace.Cmyk,
4,
inputBufferLength,
resultBufferLength,
seed);
}
var converter = JpegColorConverter.GetConverter(JpegColorSpace.Cmyk, 8);
JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed);
var result = new Vector4[resultBufferLength];
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromGrayscaleBasic(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
new JpegColorConverter.FromGrayscaleBasic(8),
1,
inputBufferLength,
resultBufferLength,
seed);
}
converter.ConvertToRgba(values, result);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromGrayscaleVector8(int inputBufferLength, int resultBufferLength, int seed)
{
if (!SimdUtils.HasVector8)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
return;
}
for (int i = 0; i < resultBufferLength; i++)
ValidateConversion(
new JpegColorConverter.FromGrayscaleVector8(8),
1,
inputBufferLength,
resultBufferLength,
seed);
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromGrayscaleAvx2(int inputBufferLength, int resultBufferLength, int seed)
{
if (!SimdUtils.HasAvx2)
{
float c = values.Component0[i];
float m = values.Component1[i];
float y = values.Component2[i];
float k = values.Component3[i] / 255F;
this.Output.WriteLine("No AVX2 present, skipping test!");
return;
}
v.X = c * k;
v.Y = m * k;
v.Z = y * k;
v.W = 1F;
ValidateConversion(
new JpegColorConverter.FromGrayscaleAvx2(8),
1,
inputBufferLength,
resultBufferLength,
seed);
}
v *= scale;
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromGraysacle_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
JpegColorSpace.Grayscale,
1,
inputBufferLength,
resultBufferLength,
seed);
}
Vector4 rgba = result[i];
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z);
var expected = new Rgb(v.X, v.Y, v.Z);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromRgbBasic(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
new JpegColorConverter.FromRgbBasic(8),
3,
inputBufferLength,
resultBufferLength,
seed);
}
Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromRgbVector8(int inputBufferLength, int resultBufferLength, int seed)
{
if (!SimdUtils.HasVector8)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
return;
}
ValidateConversion(
new JpegColorConverter.FromRgbVector8(8),
3,
inputBufferLength,
resultBufferLength,
seed);
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void ConvertFromGrayScale(int inputBufferLength, int resultBufferLength, int seed)
public void FromRgbAvx2(int inputBufferLength, int resultBufferLength, int seed)
{
var converter = JpegColorConverter.GetConverter(JpegColorSpace.Grayscale, 8);
JpegColorConverter.ComponentValues values = CreateRandomValues(1, inputBufferLength, seed);
var result = new Vector4[resultBufferLength];
if (!SimdUtils.HasAvx2)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
return;
}
converter.ConvertToRgba(values, result);
ValidateConversion(
new JpegColorConverter.FromRgbAvx2(8),
3,
inputBufferLength,
resultBufferLength,
seed);
}
for (int i = 0; i < resultBufferLength; i++)
{
float y = values.Component0[i];
Vector4 rgba = result[i];
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z);
var expected = new Rgb(y / 255F, y / 255F, y / 255F);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromRgb_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
JpegColorSpace.RGB,
3,
inputBufferLength,
resultBufferLength,
seed);
}
Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromYccKBasic(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
new JpegColorConverter.FromYccKBasic(8),
4,
inputBufferLength,
resultBufferLength,
seed);
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void ConvertFromRgb(int inputBufferLength, int resultBufferLength, int seed)
public void FromYccKVector8(int inputBufferLength, int resultBufferLength, int seed)
{
var converter = JpegColorConverter.GetConverter(JpegColorSpace.RGB, 8);
JpegColorConverter.ComponentValues values = CreateRandomValues(3, inputBufferLength, seed);
var result = new Vector4[resultBufferLength];
if (!SimdUtils.HasVector8)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
return;
}
converter.ConvertToRgba(values, result);
ValidateConversion(
new JpegColorConverter.FromYccKVector8(8),
4,
inputBufferLength,
resultBufferLength,
seed);
}
for (int i = 0; i < resultBufferLength; i++)
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromYccKAvx2(int inputBufferLength, int resultBufferLength, int seed)
{
if (!SimdUtils.HasAvx2)
{
float r = values.Component0[i];
float g = values.Component1[i];
float b = values.Component2[i];
Vector4 rgba = result[i];
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z);
var expected = new Rgb(r / 255F, g / 255F, b / 255F);
Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
this.Output.WriteLine("No AVX2 present, skipping test!");
return;
}
ValidateConversion(
new JpegColorConverter.FromYccKAvx2(8),
4,
inputBufferLength,
resultBufferLength,
seed);
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void ConvertFromYcck(int inputBufferLength, int resultBufferLength, int seed)
public void FromYcck_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
{
var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
ValidateConversion(
JpegColorSpace.Ycck,
4,
inputBufferLength,
resultBufferLength,
seed);
}
var converter = JpegColorConverter.GetConverter(JpegColorSpace.Ycck, 8);
JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed);
var result = new Vector4[resultBufferLength];
// Benchmark, for local execution only
// [Theory]
// [InlineData(false)]
// [InlineData(true)]
public void BenchmarkYCbCr(bool simd)
{
int count = 2053;
int times = 50000;
JpegColorConverter.ComponentValues values = CreateRandomValues(3, count, 1);
var result = new Vector4[count];
JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrVector(8) : new JpegColorConverter.FromYCbCrBasic(8);
// Warm up:
converter.ConvertToRgba(values, result);
for (int i = 0; i < resultBufferLength; i++)
using (new MeasureGuard(this.Output, $"{converter.GetType().Name} x {times}"))
{
float y = values.Component0[i];
float cb = values.Component1[i] - 128F;
float cr = values.Component2[i] - 128F;
float k = values.Component3[i] / 255F;
v.X = (255F - (float)Math.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k;
v.Y = (255F - (float)Math.Round(
y - (0.344136F * cb) - (0.714136F * cr),
MidpointRounding.AwayFromZero)) * k;
v.Z = (255F - (float)Math.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
v.W = 1F;
v *= scale;
Vector4 rgba = result[i];
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z);
var expected = new Rgb(v.X, v.Y, v.Z);
Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
for (int i = 0; i < times; i++)
{
converter.ConvertToRgba(values, result);
}
}
}
@ -270,7 +389,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var buffers = new Buffer2D<float>[componentCount];
for (int i = 0; i < componentCount; i++)
{
float[] values = new float[inputBufferLength];
var values = new float[inputBufferLength];
for (int j = 0; j < inputBufferLength; j++)
{
@ -293,7 +412,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
int resultBufferLength,
int seed)
{
ValidateRgbToYCbCrConversion(
ValidateConversion(
JpegColorConverter.GetConverter(colorSpace, 8),
componentCount,
inputBufferLength,
@ -301,7 +420,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
seed);
}
private static void ValidateRgbToYCbCrConversion(
private static void ValidateConversion(
JpegColorConverter converter,
int componentCount,
int inputBufferLength,
@ -315,7 +434,36 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
for (int i = 0; i < resultBufferLength; i++)
{
ValidateYCbCr(values, result, i);
Validate(converter.ColorSpace, values, result, i);
}
}
private static void Validate(
JpegColorSpace colorSpace,
in JpegColorConverter.ComponentValues values,
Vector4[] result,
int i)
{
switch (colorSpace)
{
case JpegColorSpace.Grayscale:
ValidateGrayScale(values, result, i);
break;
case JpegColorSpace.Ycck:
ValidateCyyK(values, result, i);
break;
case JpegColorSpace.Cmyk:
ValidateCmyk(values, result, i);
break;
case JpegColorSpace.RGB:
ValidateRgb(values, result, i);
break;
case JpegColorSpace.YCbCr:
ValidateYCbCr(values, result, i);
break;
default:
Assert.True(false, $"Colorspace {colorSpace} not supported!");
break;
}
}
@ -333,5 +481,81 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
}
private static void ValidateCyyK(in JpegColorConverter.ComponentValues values, Vector4[] result, int i)
{
var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
float y = values.Component0[i];
float cb = values.Component1[i] - 128F;
float cr = values.Component2[i] - 128F;
float k = values.Component3[i] / 255F;
v.X = (255F - (float)Math.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k;
v.Y = (255F - (float)Math.Round(
y - (0.344136F * cb) - (0.714136F * cr),
MidpointRounding.AwayFromZero)) * k;
v.Z = (255F - (float)Math.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
v.W = 1F;
v *= scale;
Vector4 rgba = result[i];
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z);
var expected = new Rgb(v.X, v.Y, v.Z);
Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
}
private static void ValidateRgb(in JpegColorConverter.ComponentValues values, Vector4[] result, int i)
{
float r = values.Component0[i];
float g = values.Component1[i];
float b = values.Component2[i];
Vector4 rgba = result[i];
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z);
var expected = new Rgb(r / 255F, g / 255F, b / 255F);
Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
}
private static void ValidateGrayScale(in JpegColorConverter.ComponentValues values, Vector4[] result, int i)
{
float y = values.Component0[i];
Vector4 rgba = result[i];
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z);
var expected = new Rgb(y / 255F, y / 255F, y / 255F);
Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
}
private static void ValidateCmyk(in JpegColorConverter.ComponentValues values, Vector4[] result, int i)
{
var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
float c = values.Component0[i];
float m = values.Component1[i];
float y = values.Component2[i];
float k = values.Component3[i] / 255F;
v.X = c * k;
v.Y = m * k;
v.Z = y * k;
v.W = 1F;
v *= scale;
Vector4 rgba = result[i];
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z);
var expected = new Rgb(v.X, v.Y, v.Z);
Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
}
}
}

Loading…
Cancel
Save