Browse Source

Refactor and add tests

js/color-alpha-handling
Nicolas Portmann 6 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] [Theory]
[MemberData(nameof(CommonConversionData))] [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), new JpegColorConverter.FromYCbCrBasic(8),
3, 3,
inputBufferLength, inputBufferLength,
@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[MemberData(nameof(CommonConversionData))] [MemberData(nameof(CommonConversionData))]
public void FromYCbCrVector(int inputBufferLength, int resultBufferLength, int seed) public void FromYCbCrVector(int inputBufferLength, int resultBufferLength, int seed)
{ {
ValidateRgbToYCbCrConversion( ValidateConversion(
new JpegColorConverter.FromYCbCrVector(8), new JpegColorConverter.FromYCbCrVector(8),
3, 3,
inputBufferLength, inputBufferLength,
@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
return; return;
} }
ValidateRgbToYCbCrConversion( ValidateConversion(
new JpegColorConverter.FromYCbCrVector8(8), new JpegColorConverter.FromYCbCrVector8(8),
3, 3,
inputBufferLength, inputBufferLength,
@ -92,8 +92,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
return; return;
} }
// JpegColorConverter.FromYCbCrSimdAvx2.LogPlz = s => this.Output.WriteLine(s); ValidateConversion(
ValidateRgbToYCbCrConversion(
new JpegColorConverter.FromYCbCrAvx2(8), new JpegColorConverter.FromYCbCrAvx2(8),
3, 3,
inputBufferLength, inputBufferLength,
@ -103,7 +102,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Theory] [Theory]
[MemberData(nameof(CommonConversionData))] [MemberData(nameof(CommonConversionData))]
public void ConvertFromYCbCr_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed) public void FromYCbCr_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
{ {
ValidateConversion( ValidateConversion(
JpegColorSpace.YCbCr, JpegColorSpace.YCbCr,
@ -113,149 +112,269 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
seed); seed);
} }
// Benchmark, for local execution only [Theory]
// [Theory] [MemberData(nameof(CommonConversionData))]
// [InlineData(false)] public void FromCmykBasic(int inputBufferLength, int resultBufferLength, int seed)
// [InlineData(true)]
public void BenchmarkYCbCr(bool simd)
{ {
int count = 2053; ValidateConversion(
int times = 50000; new JpegColorConverter.FromCmykBasic(8),
4,
JpegColorConverter.ComponentValues values = CreateRandomValues(3, count, 1); inputBufferLength,
var result = new Vector4[count]; 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: ValidateConversion(
converter.ConvertToRgba(values, result); 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++) this.Output.WriteLine("No AVX2 present, skipping test!");
{ return;
converter.ConvertToRgba(values, result);
}
} }
ValidateConversion(
new JpegColorConverter.FromCmykAvx2(8),
4,
inputBufferLength,
resultBufferLength,
seed);
} }
[Theory] [Theory]
[MemberData(nameof(CommonConversionData))] [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); ValidateConversion(
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); JpegColorSpace.Cmyk,
4,
inputBufferLength,
resultBufferLength,
seed);
}
var converter = JpegColorConverter.GetConverter(JpegColorSpace.Cmyk, 8); [Theory]
JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed); [MemberData(nameof(CommonConversionData))]
var result = new Vector4[resultBufferLength]; 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]; this.Output.WriteLine("No AVX2 present, skipping test!");
float m = values.Component1[i]; return;
float y = values.Component2[i]; }
float k = values.Component3[i] / 255F;
v.X = c * k; ValidateConversion(
v.Y = m * k; new JpegColorConverter.FromGrayscaleAvx2(8),
v.Z = y * k; 1,
v.W = 1F; 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]; [Theory]
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); [MemberData(nameof(CommonConversionData))]
var expected = new Rgb(v.X, v.Y, v.Z); public void FromRgbBasic(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
new JpegColorConverter.FromRgbBasic(8),
3,
inputBufferLength,
resultBufferLength,
seed);
}
Assert.Equal(expected, actual, ColorSpaceComparer); [Theory]
Assert.Equal(1, rgba.W); [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] [Theory]
[MemberData(nameof(CommonConversionData))] [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); if (!SimdUtils.HasAvx2)
JpegColorConverter.ComponentValues values = CreateRandomValues(1, inputBufferLength, seed); {
var result = new Vector4[resultBufferLength]; 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++) [Theory]
{ [MemberData(nameof(CommonConversionData))]
float y = values.Component0[i]; public void FromRgb_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
Vector4 rgba = result[i]; {
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); ValidateConversion(
var expected = new Rgb(y / 255F, y / 255F, y / 255F); JpegColorSpace.RGB,
3,
inputBufferLength,
resultBufferLength,
seed);
}
Assert.Equal(expected, actual, ColorSpaceComparer); [Theory]
Assert.Equal(1, rgba.W); [MemberData(nameof(CommonConversionData))]
} public void FromYccKBasic(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
new JpegColorConverter.FromYccKBasic(8),
4,
inputBufferLength,
resultBufferLength,
seed);
} }
[Theory] [Theory]
[MemberData(nameof(CommonConversionData))] [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); if (!SimdUtils.HasVector8)
JpegColorConverter.ComponentValues values = CreateRandomValues(3, inputBufferLength, seed); {
var result = new Vector4[resultBufferLength]; 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]; this.Output.WriteLine("No AVX2 present, skipping test!");
float g = values.Component1[i]; return;
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);
} }
ValidateConversion(
new JpegColorConverter.FromYccKAvx2(8),
4,
inputBufferLength,
resultBufferLength,
seed);
} }
[Theory] [Theory]
[MemberData(nameof(CommonConversionData))] [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); ValidateConversion(
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); JpegColorSpace.Ycck,
4,
inputBufferLength,
resultBufferLength,
seed);
}
var converter = JpegColorConverter.GetConverter(JpegColorSpace.Ycck, 8); // Benchmark, for local execution only
JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed); // [Theory]
var result = new Vector4[resultBufferLength]; // [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); 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]; for (int i = 0; i < times; i++)
float cb = values.Component1[i] - 128F; {
float cr = values.Component2[i] - 128F; converter.ConvertToRgba(values, result);
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);
} }
} }
@ -270,7 +389,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var buffers = new Buffer2D<float>[componentCount]; var buffers = new Buffer2D<float>[componentCount];
for (int i = 0; i < componentCount; i++) for (int i = 0; i < componentCount; i++)
{ {
float[] values = new float[inputBufferLength]; var values = new float[inputBufferLength];
for (int j = 0; j < inputBufferLength; j++) for (int j = 0; j < inputBufferLength; j++)
{ {
@ -293,7 +412,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
int resultBufferLength, int resultBufferLength,
int seed) int seed)
{ {
ValidateRgbToYCbCrConversion( ValidateConversion(
JpegColorConverter.GetConverter(colorSpace, 8), JpegColorConverter.GetConverter(colorSpace, 8),
componentCount, componentCount,
inputBufferLength, inputBufferLength,
@ -301,7 +420,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
seed); seed);
} }
private static void ValidateRgbToYCbCrConversion( private static void ValidateConversion(
JpegColorConverter converter, JpegColorConverter converter,
int componentCount, int componentCount,
int inputBufferLength, int inputBufferLength,
@ -315,7 +434,36 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
for (int i = 0; i < resultBufferLength; i++) 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(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W); 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