Browse Source

Merge branch 'master' into js/v2-builds

pull/1777/head
James Jackson-South 4 years ago
parent
commit
e86945e85a
  1. 4
      src/ImageSharp/Common/Helpers/InliningOptions.cs
  2. 2
      src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
  3. 47
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx2.cs
  4. 45
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykBasic.cs
  5. 34
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector8.cs
  6. 32
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx2.cs
  7. 38
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleBasic.cs
  8. 40
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx2.cs
  9. 33
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbBasic.cs
  10. 40
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector8.cs
  11. 50
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx2.cs
  12. 37
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
  13. 52
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector4.cs
  14. 47
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector8.cs
  15. 64
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx2.cs
  16. 43
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKBasic.cs
  17. 60
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector8.cs
  18. 15
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.VectorizedJpegColorConverter.cs
  19. 156
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  20. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs
  21. 64
      src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
  22. 2
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  23. 2
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  24. 2
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs
  25. 30
      src/ImageSharp/Memory/Buffer2D{T}.cs
  26. 33
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs
  27. 4
      src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs
  28. 4
      src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs
  29. 14
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
  30. 4
      src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs
  31. 6
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
  32. 6
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/CmykColorConversion.cs
  33. 3
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversionBenchmark.cs
  34. 10
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
  35. 4
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/GrayscaleColorConversion.cs
  36. 6
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/RgbColorConversion.cs
  37. 8
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs
  38. 6
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/YccKColorConverter.cs
  39. 2
      tests/ImageSharp.Benchmarks/Config.cs
  40. 3
      tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs
  41. 31
      tests/ImageSharp.Benchmarks/Processing/Resize.cs
  42. 3
      tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs
  43. 67
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
  44. 8
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
  45. 14
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
  46. 12
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs
  47. 10
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  48. 2
      tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs
  49. 26
      tests/ImageSharp.Tests/Memory/Buffer2DTests.cs
  50. 102
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
  51. 0
      tests/Images/External/ReferenceOutput/ResizeTests/CanResizeLargeImageWithCropMode_issue1006-incorrect-resize.png
  52. 0
      tests/Images/External/ReferenceOutput/ResizeTests/ResizeFromSourceRectangle_CalliphoraPartial.png
  53. 0
      tests/Images/External/ReferenceOutput/ResizeTests/ResizeHeightAndKeepAspect_CalliphoraPartial.png
  54. 0
      tests/Images/External/ReferenceOutput/ResizeTests/ResizeWidthAndKeepAspect_CalliphoraPartial.png
  55. 0
      tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithBoxPadMode_CalliphoraPartial.png
  56. 0
      tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithCropHeightMode_CalliphoraPartial.png
  57. 0
      tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithCropWidthMode_CalliphoraPartial.png
  58. 0
      tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithMaxMode_CalliphoraPartial.png
  59. 0
      tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithMinMode_CalliphoraPartial.png
  60. 0
      tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithPadMode_CalliphoraPartial.png
  61. 0
      tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithStretchMode_CalliphoraPartial.png
  62. 3
      tests/Images/External/ReferenceOutput/ResizeTests/Resize_IsNotBoundToSinglePixelType_Bgr24_TestPattern50x50.png

4
src/ImageSharp/Common/Helpers/InliningOptions.cs

@ -12,6 +12,10 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
internal static class InliningOptions internal static class InliningOptions
{ {
/// <summary>
/// <see cref="MethodImplOptions.AggressiveInlining"/> regardless of the build conditions.
/// </summary>
public const MethodImplOptions AlwaysInline = MethodImplOptions.AggressiveInlining;
#if PROFILING #if PROFILING
public const MethodImplOptions HotPath = MethodImplOptions.NoInlining; public const MethodImplOptions HotPath = MethodImplOptions.NoInlining;
public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining; public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining;

2
src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs

@ -537,7 +537,7 @@ namespace SixLabors.ImageSharp
/// <param name="vm0">The first vector to multiply.</param> /// <param name="vm0">The first vector to multiply.</param>
/// <param name="vm1">The second vector to multiply.</param> /// <param name="vm1">The second vector to multiply.</param>
/// <returns>The <see cref="Vector256{T}"/>.</returns> /// <returns>The <see cref="Vector256{T}"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.AlwaysInline)]
public static Vector256<float> MultiplyAdd( public static Vector256<float> MultiplyAdd(
in Vector256<float> va, in Vector256<float> va,
in Vector256<float> vm0, in Vector256<float> vm0,

47
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx2.cs

@ -22,60 +22,39 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS #if SUPPORTS_RUNTIME_INTRINSICS
ref Vector256<float> cBase = ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> mBase = ref Vector256<float> c1Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> yBase = ref Vector256<float> c2Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector256<float> kBase = ref Vector256<float> c3Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3));
ref Vector256<float> resultBase =
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result));
// Used for the color conversion // Used for the color conversion
var scale = Vector256.Create(1 / this.MaximumValue); var scale = Vector256.Create(1 / this.MaximumValue);
var one = Vector256.Create(1F);
// Used for packing
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32);
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control);
int n = result.Length / 8; nint n = values.Component0.Length / 8;
for (int i = 0; i < n; i++) for (nint i = 0; i < n; i++)
{ {
Vector256<float> k = Avx2.PermuteVar8x32(Unsafe.Add(ref kBase, i), vcontrol); ref Vector256<float> c = ref Unsafe.Add(ref c0Base, i);
Vector256<float> c = Avx2.PermuteVar8x32(Unsafe.Add(ref cBase, i), vcontrol); ref Vector256<float> m = ref Unsafe.Add(ref c1Base, i);
Vector256<float> m = Avx2.PermuteVar8x32(Unsafe.Add(ref mBase, i), vcontrol); ref Vector256<float> y = ref Unsafe.Add(ref c2Base, i);
Vector256<float> y = Avx2.PermuteVar8x32(Unsafe.Add(ref yBase, i), vcontrol); Vector256<float> k = Unsafe.Add(ref c3Base, i);
k = Avx.Multiply(k, scale); k = Avx.Multiply(k, scale);
c = Avx.Multiply(Avx.Multiply(c, k), scale); c = Avx.Multiply(Avx.Multiply(c, k), scale);
m = Avx.Multiply(Avx.Multiply(m, k), scale); m = Avx.Multiply(Avx.Multiply(m, k), scale);
y = Avx.Multiply(Avx.Multiply(y, k), scale); y = Avx.Multiply(Avx.Multiply(y, k), scale);
Vector256<float> cmLo = Avx.UnpackLow(c, m);
Vector256<float> yoLo = Avx.UnpackLow(y, one);
Vector256<float> cmHi = Avx.UnpackHigh(c, m);
Vector256<float> yoHi = Avx.UnpackHigh(y, one);
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4);
destination = Avx.Shuffle(cmLo, yoLo, 0b01_00_01_00);
Unsafe.Add(ref destination, 1) = Avx.Shuffle(cmLo, yoLo, 0b11_10_11_10);
Unsafe.Add(ref destination, 2) = Avx.Shuffle(cmHi, yoHi, 0b01_00_01_00);
Unsafe.Add(ref destination, 3) = Avx.Shuffle(cmHi, yoHi, 0b11_10_11_10);
} }
#endif #endif
} }
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => protected override void ConvertCoreInplace(in ComponentValues values) =>
FromCmykBasic.ConvertCore(values, result, this.MaximumValue); FromCmykBasic.ConvertCoreInplace(values, this.MaximumValue);
} }
} }
} }

45
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykBasic.cs

@ -15,38 +15,27 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result) public override void ConvertToRgbInplace(in ComponentValues values) =>
{ ConvertCoreInplace(values, this.MaximumValue);
ConvertCore(values, result, this.MaximumValue);
}
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result, float maxValue) internal static void ConvertCoreInplace(in ComponentValues values, float maxValue)
{ {
ReadOnlySpan<float> cVals = values.Component0; Span<float> c0 = values.Component0;
ReadOnlySpan<float> mVals = values.Component1; Span<float> c1 = values.Component1;
ReadOnlySpan<float> yVals = values.Component2; Span<float> c2 = values.Component2;
ReadOnlySpan<float> kVals = values.Component3; Span<float> c3 = values.Component3;
var v = new Vector4(0, 0, 0, 1F);
var maximum = 1 / maxValue;
var scale = new Vector4(maximum, maximum, maximum, 1F);
for (int i = 0; i < result.Length; i++) float scale = 1 / maxValue;
for (int i = 0; i < c0.Length; i++)
{ {
float c = cVals[i]; float c = c0[i];
float m = mVals[i]; float m = c1[i];
float y = yVals[i]; float y = c2[i];
float k = kVals[i] / maxValue; float k = c3[i] / maxValue;
v.X = c * k; c0[i] = c * k * scale;
v.Y = m * k; c1[i] = m * k * scale;
v.Z = y * k; c2[i] = y * k * scale;
v.W = 1F;
v *= scale;
result[i] = v;
} }
} }
} }

34
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector8.cs

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{ {
ref Vector<float> cBase = ref Vector<float> cBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
@ -29,43 +29,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector<float> kBase = ref Vector<float> kBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3));
ref Vector4Octet resultBase =
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result));
Vector4Pair cc = default;
Vector4Pair mm = default;
Vector4Pair yy = default;
ref Vector<float> ccRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref cc);
ref Vector<float> mmRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref mm);
ref Vector<float> yyRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref yy);
var scale = new Vector<float>(1 / this.MaximumValue); var scale = new Vector<float>(1 / this.MaximumValue);
// Walking 8 elements at one step: // Walking 8 elements at one step:
int n = result.Length / 8; nint n = values.Component0.Length / 8;
for (int i = 0; i < n; i++) for (nint i = 0; i < n; i++)
{ {
Vector<float> c = Unsafe.Add(ref cBase, i); ref Vector<float> c = ref Unsafe.Add(ref cBase, i);
Vector<float> m = Unsafe.Add(ref mBase, i); ref Vector<float> m = ref Unsafe.Add(ref mBase, i);
Vector<float> y = Unsafe.Add(ref yBase, i); ref Vector<float> y = ref Unsafe.Add(ref yBase, i);
Vector<float> k = Unsafe.Add(ref kBase, i) * scale; Vector<float> k = Unsafe.Add(ref kBase, i) * scale;
c = (c * k) * scale; c = (c * k) * scale;
m = (m * k) * scale; m = (m * k) * scale;
y = (y * k) * scale; y = (y * k) * scale;
ccRefAsVector = c;
mmRefAsVector = m;
yyRefAsVector = y;
// Collect (c0,c1...c8) (m0,m1...m8) (y0,y1...y8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i);
destination.Pack(ref cc, ref mm, ref yy);
} }
} }
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => protected override void ConvertCoreInplace(in ComponentValues values) =>
FromCmykBasic.ConvertCore(values, result, this.MaximumValue); FromCmykBasic.ConvertCoreInplace(values, this.MaximumValue);
} }
} }
} }

32
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx2.cs

@ -22,42 +22,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS #if SUPPORTS_RUNTIME_INTRINSICS
ref Vector256<float> gBase = ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> resultBase =
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result));
// Used for the color conversion // Used for the color conversion
var scale = Vector256.Create(1 / this.MaximumValue); var scale = Vector256.Create(1 / this.MaximumValue);
var one = Vector256.Create(1F);
// Used for packing
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32);
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control);
int n = result.Length / 8; nint n = values.Component0.Length / 8;
for (int i = 0; i < n; i++) for (nint i = 0; i < n; i++)
{ {
Vector256<float> g = Avx.Multiply(Unsafe.Add(ref gBase, i), scale); ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i);
c0 = Avx.Multiply(c0, scale);
g = Avx2.PermuteVar8x32(g, vcontrol);
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4);
destination = Avx.Blend(Avx.Permute(g, 0b00_00_00_00), one, 0b1000_1000);
Unsafe.Add(ref destination, 1) = Avx.Blend(Avx.Shuffle(g, g, 0b01_01_01_01), one, 0b1000_1000);
Unsafe.Add(ref destination, 2) = Avx.Blend(Avx.Shuffle(g, g, 0b10_10_10_10), one, 0b1000_1000);
Unsafe.Add(ref destination, 3) = Avx.Blend(Avx.Shuffle(g, g, 0b11_11_11_11), one, 0b1000_1000);
} }
#endif #endif
} }
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => protected override void ConvertCoreInplace(in ComponentValues values) =>
FromGrayscaleBasic.ConvertCore(values, result, this.MaximumValue); FromGrayscaleBasic.ScaleValues(values.Component0, this.MaximumValue);
} }
} }
} }

38
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleBasic.cs

@ -17,25 +17,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result) public override void ConvertToRgbInplace(in ComponentValues values) =>
{ ScaleValues(values.Component0, this.MaximumValue);
ConvertCore(values, result, this.MaximumValue);
}
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result, float maxValue) internal static void ScaleValues(Span<float> values, float maxValue)
{ {
var maximum = 1 / maxValue; Span<Vector4> vecValues = MemoryMarshal.Cast<float, Vector4>(values);
var scale = new Vector4(maximum, maximum, maximum, 1F);
ref float sBase = ref MemoryMarshal.GetReference(values.Component0); var scaleVector = new Vector4(1 / maxValue);
ref Vector4 dBase = ref MemoryMarshal.GetReference(result);
for (int i = 0; i < result.Length; i++) for (int i = 0; i < vecValues.Length; i++)
{ {
var v = new Vector4(Unsafe.Add(ref sBase, i)); vecValues[i] *= scaleVector;
v.W = 1f; }
v *= scale;
Unsafe.Add(ref dBase, i) = v; values = values.Slice(vecValues.Length * 4);
if (!values.IsEmpty)
{
float scaleValue = 1f / maxValue;
values[0] *= scaleValue;
if ((uint)values.Length > 1)
{
values[1] *= scaleValue;
if ((uint)values.Length > 2)
{
values[2] *= scaleValue;
}
}
} }
} }
} }

40
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx2.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS #if SUPPORTS_RUNTIME_INTRINSICS
ref Vector256<float> rBase = ref Vector256<float> rBase =
@ -32,41 +32,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector256<float> bBase = ref Vector256<float> bBase =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector256<float> resultBase =
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result));
// Used for the color conversion // Used for the color conversion
var scale = Vector256.Create(1 / this.MaximumValue); var scale = Vector256.Create(1 / this.MaximumValue);
var one = Vector256.Create(1F); nint n = values.Component0.Length / 8;
for (nint i = 0; i < n; i++)
// Used for packing
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32);
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control);
int n = result.Length / 8;
for (int i = 0; i < n; i++)
{ {
Vector256<float> r = Avx.Multiply(Avx2.PermuteVar8x32(Unsafe.Add(ref rBase, i), vcontrol), scale); ref Vector256<float> r = ref Unsafe.Add(ref rBase, i);
Vector256<float> g = Avx.Multiply(Avx2.PermuteVar8x32(Unsafe.Add(ref gBase, i), vcontrol), scale); ref Vector256<float> g = ref Unsafe.Add(ref gBase, i);
Vector256<float> b = Avx.Multiply(Avx2.PermuteVar8x32(Unsafe.Add(ref bBase, i), vcontrol), scale); ref Vector256<float> b = ref Unsafe.Add(ref bBase, i);
r = Avx.Multiply(r, scale);
Vector256<float> rgLo = Avx.UnpackLow(r, g); g = Avx.Multiply(g, scale);
Vector256<float> boLo = Avx.UnpackLow(b, one); b = Avx.Multiply(b, scale);
Vector256<float> rgHi = Avx.UnpackHigh(r, g);
Vector256<float> boHi = Avx.UnpackHigh(b, one);
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4);
destination = Avx.Shuffle(rgLo, boLo, 0b01_00_01_00);
Unsafe.Add(ref destination, 1) = Avx.Shuffle(rgLo, boLo, 0b11_10_11_10);
Unsafe.Add(ref destination, 2) = Avx.Shuffle(rgHi, boHi, 0b01_00_01_00);
Unsafe.Add(ref destination, 3) = Avx.Shuffle(rgHi, boHi, 0b11_10_11_10);
} }
#endif #endif
} }
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => protected override void ConvertCoreInplace(in ComponentValues values) =>
FromRgbBasic.ConvertCore(values, result, this.MaximumValue); FromRgbBasic.ConvertCoreInplace(values, this.MaximumValue);
} }
} }
} }

33
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbBasic.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
@ -15,36 +16,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result) public override void ConvertToRgbInplace(in ComponentValues values)
{ {
ConvertCore(values, result, this.MaximumValue); ConvertCoreInplace(values, this.MaximumValue);
} }
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result, float maxValue) internal static void ConvertCoreInplace(ComponentValues values, float maxValue)
{ {
ReadOnlySpan<float> rVals = values.Component0; FromGrayscaleBasic.ScaleValues(values.Component0, maxValue);
ReadOnlySpan<float> gVals = values.Component1; FromGrayscaleBasic.ScaleValues(values.Component1, maxValue);
ReadOnlySpan<float> bVals = values.Component2; FromGrayscaleBasic.ScaleValues(values.Component2, maxValue);
var v = new Vector4(0, 0, 0, 1);
var maximum = 1 / maxValue;
var scale = new Vector4(maximum, maximum, maximum, 1F);
for (int i = 0; i < result.Length; i++)
{
float r = rVals[i];
float g = gVals[i];
float b = bVals[i];
v.X = r;
v.Y = g;
v.Z = b;
v *= scale;
result[i] = v;
}
} }
} }
} }

40
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector8.cs

@ -18,50 +18,32 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{ {
ref Vector<float> rBase = ref Vector<float> rBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> gBase = ref Vector<float> gBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> bBase = ref Vector<float> bBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector4Octet resultBase =
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result));
Vector4Pair rr = default;
Vector4Pair gg = default;
Vector4Pair bb = default;
ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr);
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg);
ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb);
var scale = new Vector<float>(1 / this.MaximumValue); var scale = new Vector<float>(1 / this.MaximumValue);
// Walking 8 elements at one step: // Walking 8 elements at one step:
int n = result.Length / 8; nint n = values.Component0.Length / 8;
for (int i = 0; i < n; i++) for (nint i = 0; i < n; i++)
{ {
Vector<float> r = Unsafe.Add(ref rBase, i); ref Vector<float> r = ref Unsafe.Add(ref rBase, i);
Vector<float> g = Unsafe.Add(ref gBase, i); ref Vector<float> g = ref Unsafe.Add(ref gBase, i);
Vector<float> b = Unsafe.Add(ref bBase, i); ref Vector<float> b = ref Unsafe.Add(ref bBase, i);
r *= scale; r *= scale;
g *= scale; g *= scale;
b *= scale; b *= scale;
rrRefAsVector = r;
ggRefAsVector = g;
bbRefAsVector = b;
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i);
destination.Pack(ref rr, ref gg, ref bb);
} }
} }
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => protected override void ConvertCoreInplace(in ComponentValues values) =>
FromRgbBasic.ConvertCore(values, result, this.MaximumValue); FromRgbBasic.ConvertCoreInplace(values, this.MaximumValue);
} }
} }
} }

50
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx2.cs

@ -23,19 +23,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS #if SUPPORTS_RUNTIME_INTRINSICS
ref Vector256<float> yBase = ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> cbBase = ref Vector256<float> c1Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> crBase = ref Vector256<float> c2Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector256<float> resultBase =
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result));
// Used for the color conversion // Used for the color conversion
var chromaOffset = Vector256.Create(-this.HalfValue); var chromaOffset = Vector256.Create(-this.HalfValue);
var scale = Vector256.Create(1 / this.MaximumValue); var scale = Vector256.Create(1 / this.MaximumValue);
@ -50,19 +47,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control); Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control);
// Walking 8 elements at one step: // Walking 8 elements at one step:
int n = result.Length / 8; nint n = values.Component0.Length / 8;
for (int i = 0; i < n; i++) for (nint i = 0; i < n; i++)
{ {
// y = yVals[i]; // y = yVals[i];
// cb = cbVals[i] - 128F; // cb = cbVals[i] - 128F;
// cr = crVals[i] - 128F; // cr = crVals[i] - 128F;
Vector256<float> y = Unsafe.Add(ref yBase, i); ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i);
Vector256<float> cb = Avx.Add(Unsafe.Add(ref cbBase, i), chromaOffset); ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i);
Vector256<float> cr = Avx.Add(Unsafe.Add(ref crBase, i), chromaOffset); ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i);
y = Avx2.PermuteVar8x32(y, vcontrol); Vector256<float> y = c0;
cb = Avx2.PermuteVar8x32(cb, vcontrol); Vector256<float> cb = Avx.Add(c1, chromaOffset);
cr = Avx2.PermuteVar8x32(cr, vcontrol); Vector256<float> cr = Avx.Add(c2, chromaOffset);
// r = y + (1.402F * cr); // r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr); // g = y - (0.344136F * cb) - (0.714136F * cr);
@ -72,30 +69,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
Vector256<float> g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); Vector256<float> g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult);
// TODO: We should be saving to RGBA not Vector4
r = Avx.Multiply(Avx.RoundToNearestInteger(r), scale); r = Avx.Multiply(Avx.RoundToNearestInteger(r), scale);
g = Avx.Multiply(Avx.RoundToNearestInteger(g), scale); g = Avx.Multiply(Avx.RoundToNearestInteger(g), scale);
b = Avx.Multiply(Avx.RoundToNearestInteger(b), scale); b = Avx.Multiply(Avx.RoundToNearestInteger(b), scale);
Vector256<float> vte = Avx.UnpackLow(r, b); c0 = r;
Vector256<float> vto = Avx.UnpackLow(g, va); c1 = g;
c2 = b;
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4);
destination = Avx.UnpackLow(vte, vto);
Unsafe.Add(ref destination, 1) = Avx.UnpackHigh(vte, vto);
vte = Avx.UnpackHigh(r, b);
vto = Avx.UnpackHigh(g, va);
Unsafe.Add(ref destination, 2) = Avx.UnpackLow(vte, vto);
Unsafe.Add(ref destination, 3) = Avx.UnpackHigh(vte, vto);
} }
#endif #endif
} }
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => protected override void ConvertCoreInplace(in ComponentValues values) =>
FromYCbCrBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); FromYCbCrBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
} }
} }
} }

37
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs

@ -15,35 +15,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result) public override void ConvertToRgbInplace(in ComponentValues values)
{ => ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
ConvertCore(values, result, this.MaximumValue, this.HalfValue);
}
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result, float maxValue, float halfValue) internal static void ConvertCoreInplace(in ComponentValues values, float maxValue, float halfValue)
{ {
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()! Span<float> c0 = values.Component0;
ReadOnlySpan<float> yVals = values.Component0; Span<float> c1 = values.Component1;
ReadOnlySpan<float> cbVals = values.Component1; Span<float> c2 = values.Component2;
ReadOnlySpan<float> crVals = values.Component2;
var v = new Vector4(0, 0, 0, 1);
var scale = new Vector4(1 / maxValue, 1 / maxValue, 1 / maxValue, 1F); var scale = 1 / maxValue;
for (int i = 0; i < result.Length; i++) for (int i = 0; i < c0.Length; i++)
{ {
float y = yVals[i]; float y = c0[i];
float cb = cbVals[i] - halfValue; float cb = c1[i] - halfValue;
float cr = crVals[i] - halfValue; float cr = c2[i] - halfValue;
v.X = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero);
v.Y = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero);
v.Z = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero);
v *= scale;
result[i] = v; c0[i] = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero) * scale;
c1[i] = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero) * scale;
c2[i] = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero) * scale;
} }
} }
} }

52
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector4.cs

@ -20,58 +20,54 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
protected override bool IsAvailable => SimdUtils.HasVector4; protected override bool IsAvailable => SimdUtils.HasVector4;
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{ {
// TODO: Find a way to properly run & test this path on AVX2 PC-s! (Have I already mentioned that Vector<T> is terrible?) DebugGuard.IsTrue(values.Component0.Length % 8 == 0, nameof(values), "Length should be divisible by 8!");
DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisible by 8!");
ref Vector4Pair yBase = ref Vector4Pair c0Base =
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component0)); ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector4Pair cbBase = ref Vector4Pair c1Base =
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component1)); ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector4Pair crBase = ref Vector4Pair c2Base =
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component2)); ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector4Octet resultBase =
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result));
var chromaOffset = new Vector4(-this.HalfValue); var chromaOffset = new Vector4(-this.HalfValue);
var maxValue = this.MaximumValue; var maxValue = this.MaximumValue;
// Walking 8 elements at one step: // Walking 8 elements at one step:
int n = result.Length / 8; nint n = values.Component0.Length / 8;
for (int i = 0; i < n; i++) for (nint i = 0; i < n; i++)
{ {
// y = yVals[i]; // y = yVals[i];
Vector4Pair y = Unsafe.Add(ref yBase, i); ref Vector4Pair c0 = ref Unsafe.Add(ref c0Base, i);
// cb = cbVals[i] - halfValue); // cb = cbVals[i] - halfValue);
Vector4Pair cb = Unsafe.Add(ref cbBase, i); ref Vector4Pair c1 = ref Unsafe.Add(ref c1Base, i);
cb.AddInplace(chromaOffset); c1.AddInplace(chromaOffset);
// cr = crVals[i] - halfValue; // cr = crVals[i] - halfValue;
Vector4Pair cr = Unsafe.Add(ref crBase, i); ref Vector4Pair c2 = ref Unsafe.Add(ref c2Base, i);
cr.AddInplace(chromaOffset); c2.AddInplace(chromaOffset);
// r = y + (1.402F * cr); // r = y + (1.402F * cr);
Vector4Pair r = y; Vector4Pair r = c0;
Vector4Pair tmp = cr; Vector4Pair tmp = c2;
tmp.MultiplyInplace(1.402F); tmp.MultiplyInplace(1.402F);
r.AddInplace(ref tmp); r.AddInplace(ref tmp);
// g = y - (0.344136F * cb) - (0.714136F * cr); // g = y - (0.344136F * cb) - (0.714136F * cr);
Vector4Pair g = y; Vector4Pair g = c0;
tmp = cb; tmp = c1;
tmp.MultiplyInplace(-0.344136F); tmp.MultiplyInplace(-0.344136F);
g.AddInplace(ref tmp); g.AddInplace(ref tmp);
tmp = cr; tmp = c2;
tmp.MultiplyInplace(-0.714136F); tmp.MultiplyInplace(-0.714136F);
g.AddInplace(ref tmp); g.AddInplace(ref tmp);
// b = y + (1.772F * cb); // b = y + (1.772F * cb);
Vector4Pair b = y; Vector4Pair b = c0;
tmp = cb; tmp = c1;
tmp.MultiplyInplace(1.772F); tmp.MultiplyInplace(1.772F);
b.AddInplace(ref tmp); b.AddInplace(ref tmp);
@ -79,14 +75,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
g.RoundAndDownscalePreVector8(maxValue); g.RoundAndDownscalePreVector8(maxValue);
b.RoundAndDownscalePreVector8(maxValue); b.RoundAndDownscalePreVector8(maxValue);
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order: c0 = r;
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); c1 = g;
destination.Pack(ref r, ref g, ref b); c2 = b;
} }
} }
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => protected override void ConvertCoreInplace(in ComponentValues values)
FromYCbCrBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); => FromYCbCrBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
} }
} }
} }

47
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector8.cs

@ -19,41 +19,32 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{ {
ref Vector<float> yBase = ref Vector<float> c0Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> cbBase = ref Vector<float> c1Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> crBase = ref Vector<float> c2Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector4Octet resultBase =
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result));
var chromaOffset = new Vector<float>(-this.HalfValue); var chromaOffset = new Vector<float>(-this.HalfValue);
// Walking 8 elements at one step: // Walking 8 elements at one step:
int n = result.Length / 8; nint n = values.Component0.Length / 8;
Vector4Pair rr = default;
Vector4Pair gg = default;
Vector4Pair bb = default;
ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr);
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg);
ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb);
var scale = new Vector<float>(1 / this.MaximumValue); var scale = new Vector<float>(1 / this.MaximumValue);
for (int i = 0; i < n; i++) for (nint i = 0; i < n; i++)
{ {
// y = yVals[i]; // y = yVals[i];
// cb = cbVals[i] - 128F; // cb = cbVals[i] - 128F;
// cr = crVals[i] - 128F; // cr = crVals[i] - 128F;
Vector<float> y = Unsafe.Add(ref yBase, i); ref Vector<float> c0 = ref Unsafe.Add(ref c0Base, i);
Vector<float> cb = Unsafe.Add(ref cbBase, i) + chromaOffset; ref Vector<float> c1 = ref Unsafe.Add(ref c1Base, i);
Vector<float> cr = Unsafe.Add(ref crBase, i) + chromaOffset; ref Vector<float> c2 = ref Unsafe.Add(ref c2Base, i);
Vector<float> y = Unsafe.Add(ref c0Base, i);
Vector<float> cb = Unsafe.Add(ref c1Base, i) + chromaOffset;
Vector<float> cr = Unsafe.Add(ref c2Base, i) + chromaOffset;
// r = y + (1.402F * cr); // r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr); // g = y - (0.344136F * cb) - (0.714136F * cr);
@ -70,18 +61,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
g *= scale; g *= scale;
b *= scale; b *= scale;
rrRefAsVector = r; c0 = r;
ggRefAsVector = g; c1 = g;
bbRefAsVector = b; c2 = b;
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i);
destination.Pack(ref rr, ref gg, ref bb);
} }
} }
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => protected override void ConvertCoreInplace(in ComponentValues values) =>
FromYCbCrBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); FromYCbCrBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
} }
} }
} }

64
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx2.cs

@ -22,52 +22,42 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS #if SUPPORTS_RUNTIME_INTRINSICS
ref Vector256<float> yBase = ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> cbBase = ref Vector256<float> c1Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> crBase = ref Vector256<float> c2Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector256<float> kBase = ref Vector256<float> kBase =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3));
ref Vector256<float> resultBase =
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result));
// Used for the color conversion // Used for the color conversion
var chromaOffset = Vector256.Create(-this.HalfValue); var chromaOffset = Vector256.Create(-this.HalfValue);
var scale = Vector256.Create(1 / this.MaximumValue); var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue));
var max = Vector256.Create(this.MaximumValue); var max = Vector256.Create(this.MaximumValue);
var rCrMult = Vector256.Create(1.402F); var rCrMult = Vector256.Create(1.402F);
var gCbMult = Vector256.Create(-0.344136F); var gCbMult = Vector256.Create(-0.344136F);
var gCrMult = Vector256.Create(-0.714136F); var gCrMult = Vector256.Create(-0.714136F);
var bCbMult = Vector256.Create(1.772F); var bCbMult = Vector256.Create(1.772F);
// Used for packing.
var va = Vector256.Create(1F);
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32);
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control);
// Walking 8 elements at one step: // Walking 8 elements at one step:
int n = result.Length / 8; nint n = values.Component0.Length / 8;
for (int i = 0; i < n; i++) for (nint i = 0; i < n; i++)
{ {
// y = yVals[i]; // y = yVals[i];
// cb = cbVals[i] - 128F; // cb = cbVals[i] - 128F;
// cr = crVals[i] - 128F; // cr = crVals[i] - 128F;
// k = kVals[i] / 256F; // k = kVals[i] / 256F;
Vector256<float> y = Unsafe.Add(ref yBase, i); ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i);
Vector256<float> cb = Avx.Add(Unsafe.Add(ref cbBase, i), chromaOffset); ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i);
Vector256<float> cr = Avx.Add(Unsafe.Add(ref crBase, i), chromaOffset); ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i);
Vector256<float> k = Avx.Divide(Unsafe.Add(ref kBase, i), max); Vector256<float> y = c0;
Vector256<float> cb = Avx.Add(c1, chromaOffset);
y = Avx2.PermuteVar8x32(y, vcontrol); Vector256<float> cr = Avx.Add(c2, chromaOffset);
cb = Avx2.PermuteVar8x32(cb, vcontrol); Vector256<float> scaledK = Avx.Multiply(Unsafe.Add(ref kBase, i), scale);
cr = Avx2.PermuteVar8x32(cr, vcontrol);
k = Avx2.PermuteVar8x32(k, vcontrol);
// r = y + (1.402F * cr); // r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr); // g = y - (0.344136F * cb) - (0.714136F * cr);
@ -82,29 +72,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
g = Avx.Subtract(max, Avx.RoundToNearestInteger(g)); g = Avx.Subtract(max, Avx.RoundToNearestInteger(g));
b = Avx.Subtract(max, Avx.RoundToNearestInteger(b)); b = Avx.Subtract(max, Avx.RoundToNearestInteger(b));
r = Avx.Multiply(Avx.Multiply(r, k), scale); r = Avx.Multiply(r, scaledK);
g = Avx.Multiply(Avx.Multiply(g, k), scale); g = Avx.Multiply(g, scaledK);
b = Avx.Multiply(Avx.Multiply(b, k), scale); b = Avx.Multiply(b, scaledK);
Vector256<float> vte = Avx.UnpackLow(r, b);
Vector256<float> vto = Avx.UnpackLow(g, va);
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4);
destination = Avx.UnpackLow(vte, vto);
Unsafe.Add(ref destination, 1) = Avx.UnpackHigh(vte, vto);
vte = Avx.UnpackHigh(r, b);
vto = Avx.UnpackHigh(g, va);
Unsafe.Add(ref destination, 2) = Avx.UnpackLow(vte, vto); c0 = r;
Unsafe.Add(ref destination, 3) = Avx.UnpackHigh(vte, vto); c1 = g;
c2 = b;
} }
#endif #endif
} }
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => protected override void ConvertCoreInplace(in ComponentValues values) =>
FromYccKBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); FromYccKBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
} }
} }
} }

43
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKBasic.cs

@ -15,39 +15,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result) public override void ConvertToRgbInplace(in ComponentValues values) =>
{ ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
ConvertCore(values, result, this.MaximumValue, this.HalfValue);
}
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result, float maxValue, float halfValue) internal static void ConvertCoreInplace(in ComponentValues values, float maxValue, float halfValue)
{ {
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()! Span<float> c0 = values.Component0;
ReadOnlySpan<float> yVals = values.Component0; Span<float> c1 = values.Component1;
ReadOnlySpan<float> cbVals = values.Component1; Span<float> c2 = values.Component2;
ReadOnlySpan<float> crVals = values.Component2; Span<float> c3 = values.Component3;
ReadOnlySpan<float> kVals = values.Component3;
var v = new Vector4(0, 0, 0, 1F); var v = new Vector4(0, 0, 0, 1F);
var maximum = 1 / maxValue; var scale = 1 / (maxValue * maxValue);
var scale = new Vector4(maximum, maximum, maximum, 1F);
for (int i = 0; i < result.Length; i++) for (int i = 0; i < values.Component0.Length; i++)
{ {
float y = yVals[i]; float y = c0[i];
float cb = cbVals[i] - halfValue; float cb = c1[i] - halfValue;
float cr = crVals[i] - halfValue; float cr = c2[i] - halfValue;
float k = kVals[i] / maxValue; float scaledK = c3[i] * scale;
v.X = (maxValue - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k; c0[i] = (maxValue - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * scaledK;
v.Y = (maxValue - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k; c1[i] = (maxValue - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * scaledK;
v.Z = (maxValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k; c2[i] = (maxValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * scaledK;
v.W = 1F;
v *= scale;
result[i] = v;
} }
} }
} }

60
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector8.cs

@ -18,46 +18,39 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{ {
} }
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{ {
ref Vector<float> yBase = ref Vector<float> c0Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> cbBase = ref Vector<float> c1Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> crBase = ref Vector<float> c2Base =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector<float> kBase = ref Vector<float> kBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3));
ref Vector4Octet resultBase =
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result));
var chromaOffset = new Vector<float>(-this.HalfValue); var chromaOffset = new Vector<float>(-this.HalfValue);
// Walking 8 elements at one step: // Walking 8 elements at one step:
int n = result.Length / 8; nint n = values.Component0.Length / 8;
Vector4Pair rr = default;
Vector4Pair gg = default;
Vector4Pair bb = default;
ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr);
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg);
ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb);
var scale = new Vector<float>(1 / this.MaximumValue);
var max = new Vector<float>(this.MaximumValue); var max = new Vector<float>(this.MaximumValue);
var scale = new Vector<float>(1f) / (max * max);
for (int i = 0; i < n; i++) for (nint i = 0; i < n; i++)
{ {
// y = yVals[i]; // y = yVals[i];
// cb = cbVals[i] - 128F; // cb = cbVals[i] - 128F;
// cr = crVals[i] - 128F; // cr = crVals[i] - 128F;
// k = kVals[i] / 256F; // k = kVals[i] / 256F;
Vector<float> y = Unsafe.Add(ref yBase, i); ref Vector<float> c0 = ref Unsafe.Add(ref c0Base, i);
Vector<float> cb = Unsafe.Add(ref cbBase, i) + chromaOffset; ref Vector<float> c1 = ref Unsafe.Add(ref c1Base, i);
Vector<float> cr = Unsafe.Add(ref crBase, i) + chromaOffset; ref Vector<float> c2 = ref Unsafe.Add(ref c2Base, i);
Vector<float> k = Unsafe.Add(ref kBase, i) / max;
Vector<float> y = c0;
Vector<float> cb = c1 + chromaOffset;
Vector<float> cr = c2 + chromaOffset;
Vector<float> scaledK = Unsafe.Add(ref kBase, i) * scale;
// r = y + (1.402F * cr); // r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr); // g = y - (0.344136F * cb) - (0.714136F * cr);
@ -67,25 +60,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
Vector<float> g = y - (cb * new Vector<float>(0.344136F)) - (cr * new Vector<float>(0.714136F)); Vector<float> g = y - (cb * new Vector<float>(0.344136F)) - (cr * new Vector<float>(0.714136F));
Vector<float> b = y + (cb * new Vector<float>(1.772F)); Vector<float> b = y + (cb * new Vector<float>(1.772F));
r = (max - r.FastRound()) * k; r = (max - r.FastRound()) * scaledK;
g = (max - g.FastRound()) * k; g = (max - g.FastRound()) * scaledK;
b = (max - b.FastRound()) * k; b = (max - b.FastRound()) * scaledK;
r *= scale;
g *= scale;
b *= scale;
rrRefAsVector = r;
ggRefAsVector = g;
bbRefAsVector = b;
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order: c0 = r;
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); c1 = g;
destination.Pack(ref rr, ref gg, ref bb); c2 = b;
} }
} }
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => protected override void ConvertCoreInplace(in ComponentValues values) =>
FromYccKBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); FromYccKBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
} }
} }
} }

15
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.VectorizedJpegColorConverter.cs

@ -18,10 +18,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
this.vectorSize = vectorSize; this.vectorSize = vectorSize;
} }
public sealed override void ConvertToRgba(in ComponentValues values, Span<Vector4> result) public override void ConvertToRgbInplace(in ComponentValues values)
{ {
int remainder = result.Length % this.vectorSize; int length = values.Component0.Length;
int simdCount = result.Length - remainder; int remainder = values.Component0.Length % this.vectorSize;
int simdCount = length - remainder;
if (simdCount > 0) if (simdCount > 0)
{ {
// This implementation is actually AVX specific. // This implementation is actually AVX specific.
@ -32,15 +33,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
"This converter can be used only on architecture having 256 byte floating point SIMD registers!"); "This converter can be used only on architecture having 256 byte floating point SIMD registers!");
} }
this.ConvertCoreVectorized(values.Slice(0, simdCount), result.Slice(0, simdCount)); this.ConvertCoreVectorizedInplace(values.Slice(0, simdCount));
} }
this.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder)); this.ConvertCoreInplace(values.Slice(simdCount, remainder));
} }
protected abstract void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result); protected virtual void ConvertCoreVectorizedInplace(in ComponentValues values) => throw new NotImplementedException();
protected abstract void ConvertCore(in ComponentValues values, Span<Vector4> result); protected virtual void ConvertCoreInplace(in ComponentValues values) => throw new NotImplementedException();
} }
} }
} }

156
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs

@ -76,11 +76,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
} }
/// <summary> /// <summary>
/// He implementation of the conversion. /// Converts planar jpeg component values in <paramref name="values"/> to RGB color space inplace.
/// </summary> /// </summary>
/// <param name="values">The input as a stack-only <see cref="ComponentValues"/> struct</param> /// <param name="values">The input/ouptut as a stack-only <see cref="ComponentValues"/> struct</param>
/// <param name="result">The destination buffer of <see cref="Vector4"/> values</param> public abstract void ConvertToRgbInplace(in ComponentValues values);
public abstract void ConvertToRgba(in ComponentValues values, Span<Vector4> result);
/// <summary> /// <summary>
/// Returns the <see cref="JpegColorConverter"/>s for all supported colorspaces and precisions. /// Returns the <see cref="JpegColorConverter"/>s for all supported colorspaces and precisions.
@ -181,22 +180,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// <summary> /// <summary>
/// The component 0 (eg. Y) /// The component 0 (eg. Y)
/// </summary> /// </summary>
public readonly ReadOnlySpan<float> Component0; public readonly Span<float> Component0;
/// <summary> /// <summary>
/// The component 1 (eg. Cb) /// The component 1 (eg. Cb). In case of grayscale, it points to <see cref="Component0"/>.
/// </summary> /// </summary>
public readonly ReadOnlySpan<float> Component1; public readonly Span<float> Component1;
/// <summary> /// <summary>
/// The component 2 (eg. Cr) /// The component 2 (eg. Cr). In case of grayscale, it points to <see cref="Component0"/>.
/// </summary> /// </summary>
public readonly ReadOnlySpan<float> Component2; public readonly Span<float> Component2;
/// <summary> /// <summary>
/// The component 4 /// The component 4
/// </summary> /// </summary>
public readonly ReadOnlySpan<float> Component3; public readonly Span<float> Component3;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ComponentValues"/> struct. /// Initializes a new instance of the <see cref="ComponentValues"/> struct.
@ -208,30 +207,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
this.ComponentCount = componentBuffers.Count; this.ComponentCount = componentBuffers.Count;
this.Component0 = componentBuffers[0].GetRowSpan(row); this.Component0 = componentBuffers[0].GetRowSpan(row);
this.Component1 = Span<float>.Empty;
this.Component2 = Span<float>.Empty; // In case of grayscale, Component1 and Component2 point to Component0 memory area
this.Component3 = Span<float>.Empty; this.Component1 = this.ComponentCount > 1 ? componentBuffers[1].GetRowSpan(row) : this.Component0;
this.Component2 = this.ComponentCount > 2 ? componentBuffers[2].GetRowSpan(row) : this.Component0;
if (this.ComponentCount > 1) this.Component3 = this.ComponentCount > 3 ? componentBuffers[3].GetRowSpan(row) : Span<float>.Empty;
{
this.Component1 = componentBuffers[1].GetRowSpan(row);
if (this.ComponentCount > 2)
{
this.Component2 = componentBuffers[2].GetRowSpan(row);
if (this.ComponentCount > 3)
{
this.Component3 = componentBuffers[3].GetRowSpan(row);
}
}
}
} }
private ComponentValues( internal ComponentValues(
int componentCount, int componentCount,
ReadOnlySpan<float> c0, Span<float> c0,
ReadOnlySpan<float> c1, Span<float> c1,
ReadOnlySpan<float> c2, Span<float> c2,
ReadOnlySpan<float> c3) Span<float> c3)
{ {
this.ComponentCount = componentCount; this.ComponentCount = componentCount;
this.Component0 = c0; this.Component0 = c0;
@ -242,111 +230,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
public ComponentValues Slice(int start, int length) public ComponentValues Slice(int start, int length)
{ {
ReadOnlySpan<float> c0 = this.Component0.Slice(start, length); Span<float> c0 = this.Component0.Slice(start, length);
ReadOnlySpan<float> c1 = this.ComponentCount > 1 ? this.Component1.Slice(start, length) : ReadOnlySpan<float>.Empty; Span<float> c1 = this.Component1.Length > 0 ? this.Component1.Slice(start, length) : Span<float>.Empty;
ReadOnlySpan<float> c2 = this.ComponentCount > 2 ? this.Component2.Slice(start, length) : ReadOnlySpan<float>.Empty; Span<float> c2 = this.Component2.Length > 0 ? this.Component2.Slice(start, length) : Span<float>.Empty;
ReadOnlySpan<float> c3 = this.ComponentCount > 3 ? this.Component3.Slice(start, length) : ReadOnlySpan<float>.Empty; Span<float> c3 = this.Component3.Length > 0 ? this.Component3.Slice(start, length) : Span<float>.Empty;
return new ComponentValues(this.ComponentCount, c0, c1, c2, c3); return new ComponentValues(this.ComponentCount, c0, c1, c2, c3);
} }
} }
internal struct Vector4Octet
{
#pragma warning disable SA1132 // Do not combine fields
public Vector4 V0, V1, V2, V3, V4, V5, V6, V7;
/// <summary>
/// Pack (r0,r1...r7) (g0,g1...g7) (b0,b1...b7) vector values as (r0,g0,b0,1), (r1,g1,b1,1) ...
/// </summary>
public void Pack(ref Vector4Pair r, ref Vector4Pair g, ref Vector4Pair b)
{
this.V0.X = r.A.X;
this.V0.Y = g.A.X;
this.V0.Z = b.A.X;
this.V0.W = 1f;
this.V1.X = r.A.Y;
this.V1.Y = g.A.Y;
this.V1.Z = b.A.Y;
this.V1.W = 1f;
this.V2.X = r.A.Z;
this.V2.Y = g.A.Z;
this.V2.Z = b.A.Z;
this.V2.W = 1f;
this.V3.X = r.A.W;
this.V3.Y = g.A.W;
this.V3.Z = b.A.W;
this.V3.W = 1f;
this.V4.X = r.B.X;
this.V4.Y = g.B.X;
this.V4.Z = b.B.X;
this.V4.W = 1f;
this.V5.X = r.B.Y;
this.V5.Y = g.B.Y;
this.V5.Z = b.B.Y;
this.V5.W = 1f;
this.V6.X = r.B.Z;
this.V6.Y = g.B.Z;
this.V6.Z = b.B.Z;
this.V6.W = 1f;
this.V7.X = r.B.W;
this.V7.Y = g.B.W;
this.V7.Z = b.B.W;
this.V7.W = 1f;
}
/// <summary>
/// Pack (g0,g1...g7) vector values as (g0,g0,g0,1), (g1,g1,g1,1) ...
/// </summary>
public void Pack(ref Vector4Pair g)
{
this.V0.X = g.A.X;
this.V0.Y = g.A.X;
this.V0.Z = g.A.X;
this.V0.W = 1f;
this.V1.X = g.A.Y;
this.V1.Y = g.A.Y;
this.V1.Z = g.A.Y;
this.V1.W = 1f;
this.V2.X = g.A.Z;
this.V2.Y = g.A.Z;
this.V2.Z = g.A.Z;
this.V2.W = 1f;
this.V3.X = g.A.W;
this.V3.Y = g.A.W;
this.V3.Z = g.A.W;
this.V3.W = 1f;
this.V4.X = g.B.X;
this.V4.Y = g.B.X;
this.V4.Z = g.B.X;
this.V4.W = 1f;
this.V5.X = g.B.Y;
this.V5.Y = g.B.Y;
this.V5.Z = g.B.Y;
this.V5.W = 1f;
this.V6.X = g.B.Z;
this.V6.Y = g.B.Z;
this.V6.Z = g.B.Z;
this.V6.W = 1f;
this.V7.X = g.B.W;
this.V7.Y = g.B.W;
this.V7.Z = g.B.W;
this.V7.W = 1f;
}
}
} }
} }

6
src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs

@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool HasBadMarker() => this.Marker != JpegConstants.Markers.XFF && !this.HasRestartMarker(); public bool HasBadMarker() => this.Marker != JpegConstants.Markers.XFF && !this.HasRestartMarker();
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.AlwaysInline)]
public void FillBuffer() public void FillBuffer()
{ {
// Attempt to load at least the minimum number of required bits into the buffer. // Attempt to load at least the minimum number of required bits into the buffer.
@ -130,7 +130,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public int PeekBits(int nbits) => (int)ExtractBits(this.data, this.remainingBits - nbits, nbits); public int PeekBits(int nbits) => (int)ExtractBits(this.data, this.remainingBits - nbits, nbits);
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.AlwaysInline)]
private static ulong ExtractBits(ulong value, int offset, int size) => (value >> offset) & (ulong)((1 << size) - 1); private static ulong ExtractBits(ulong value, int offset, int size) => (value >> offset) & (ulong)((1 << size) - 1);
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.AlwaysInline)]
private int ReadStream() private int ReadStream()
{ {
int value = this.badData ? 0 : this.stream.ReadByte(); int value = this.badData ? 0 : this.stream.ReadByte();

64
src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs

@ -22,7 +22,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private JpegColorConverter colorConverter; private JpegColorConverter colorConverter;
private IMemoryOwner<Vector4> rgbaBuffer; // private IMemoryOwner<Vector4> rgbaBuffer;
private IMemoryOwner<byte> rgbBuffer;
private IMemoryOwner<TPixel> paddedProxyPixelRow;
private Buffer2D<TPixel> pixelBuffer; private Buffer2D<TPixel> pixelBuffer;
@ -40,23 +43,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private bool Converted => this.pixelRowCounter >= this.pixelBuffer.Height; private bool Converted => this.pixelRowCounter >= this.pixelBuffer.Height;
public Buffer2D<TPixel> PixelBuffer public Buffer2D<TPixel> GetPixelBuffer()
{ {
get if (!this.Converted)
{ {
if (!this.Converted) int steps = (int)Math.Ceiling(this.pixelBuffer.Height / (float)this.pixelRowsPerStep);
{
int steps = (int)Math.Ceiling(this.pixelBuffer.Height / (float)this.pixelRowsPerStep);
for (int step = 0; step < steps; step++) for (int step = 0; step < steps; step++)
{ {
this.cancellationToken.ThrowIfCancellationRequested(); this.cancellationToken.ThrowIfCancellationRequested();
this.ConvertNextStride(step); this.ConvertNextStride(step);
}
} }
return this.pixelBuffer;
} }
return this.pixelBuffer;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -72,7 +72,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.pixelRowsPerStep = this.blockRowsPerStep * blockPixelHeight; this.pixelRowsPerStep = this.blockRowsPerStep * blockPixelHeight;
// pixel buffer for resulting image // pixel buffer for resulting image
this.pixelBuffer = allocator.Allocate2D<TPixel>(frame.PixelWidth, frame.PixelHeight, AllocationOptions.Clean); this.pixelBuffer = allocator.Allocate2D<TPixel>(frame.PixelWidth, frame.PixelHeight);
this.paddedProxyPixelRow = allocator.Allocate<TPixel>(frame.PixelWidth + 3);
// component processors from spectral to Rgba32 // component processors from spectral to Rgba32
var postProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, this.pixelRowsPerStep); var postProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, this.pixelRowsPerStep);
@ -83,7 +84,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
} }
// single 'stride' rgba32 buffer for conversion between spectral and TPixel // single 'stride' rgba32 buffer for conversion between spectral and TPixel
this.rgbaBuffer = allocator.Allocate<Vector4>(frame.PixelWidth); // this.rgbaBuffer = allocator.Allocate<Vector4>(frame.PixelWidth);
this.rgbBuffer = allocator.Allocate<byte>(frame.PixelWidth * 3);
// color converter from Rgba32 to TPixel // color converter from Rgba32 to TPixel
this.colorConverter = this.GetColorConverter(frame, jpegData); this.colorConverter = this.GetColorConverter(frame, jpegData);
@ -115,7 +117,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
} }
} }
this.rgbaBuffer?.Dispose(); this.rgbBuffer?.Dispose();
this.paddedProxyPixelRow?.Dispose();
} }
private void ConvertNextStride(int spectralStep) private void ConvertNextStride(int spectralStep)
@ -129,17 +132,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
buffers[i] = this.componentProcessors[i].ColorBuffer; buffers[i] = this.componentProcessors[i].ColorBuffer;
} }
int width = this.pixelBuffer.Width;
for (int yy = this.pixelRowCounter; yy < maxY; yy++) for (int yy = this.pixelRowCounter; yy < maxY; yy++)
{ {
int y = yy - this.pixelRowCounter; int y = yy - this.pixelRowCounter;
var values = new JpegColorConverter.ComponentValues(buffers, y); var values = new JpegColorConverter.ComponentValues(buffers, y);
this.colorConverter.ConvertToRgba(values, this.rgbaBuffer.GetSpan());
Span<TPixel> destRow = this.pixelBuffer.GetRowSpan(yy); this.colorConverter.ConvertToRgbInplace(values);
values = values.Slice(0, width); // slice away Jpeg padding
// TODO: Investigate if slicing is actually necessary Span<byte> r = this.rgbBuffer.Slice(0, width);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, this.rgbaBuffer.GetSpan().Slice(0, destRow.Length), destRow); Span<byte> g = this.rgbBuffer.Slice(width, width);
Span<byte> b = this.rgbBuffer.Slice(width * 2, width);
SimdUtils.NormalizedFloatToByteSaturate(values.Component0, r);
SimdUtils.NormalizedFloatToByteSaturate(values.Component1, g);
SimdUtils.NormalizedFloatToByteSaturate(values.Component2, b);
// PackFromRgbPlanes expects the destination to be padded, so try to get padded span containing extra elements from the next row.
// If we can't get such a padded row because we are on a MemoryGroup boundary or at the last row,
// pack pixels to a temporary, padded proxy buffer, then copy the relevant values to the destination row.
if (this.pixelBuffer.TryGetPaddedRowSpan(yy, 3, out Span<TPixel> destRow))
{
PixelOperations<TPixel>.Instance.PackFromRgbPlanes(this.configuration, r, g, b, destRow);
}
else
{
Span<TPixel> proxyRow = this.paddedProxyPixelRow.GetSpan();
PixelOperations<TPixel>.Instance.PackFromRgbPlanes(this.configuration, r, g, b, proxyRow);
proxyRow.Slice(0, width).CopyTo(this.pixelBuffer.GetRowSpan(yy));
}
} }
this.pixelRowCounter += this.pixelRowsPerStep; this.pixelRowCounter += this.pixelRowsPerStep;

2
src/ImageSharp/Formats/Jpeg/JpegDecoder.cs

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <inheritdoc /> /// <inheritdoc />
public Image Decode(Configuration configuration, Stream stream) public Image Decode(Configuration configuration, Stream stream)
=> this.Decode<Rgba32>(configuration, stream); => this.Decode<Rgb24>(configuration, stream);
/// <inheritdoc/> /// <inheritdoc/>
public Task<Image<TPixel>> DecodeAsync<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken) public Task<Image<TPixel>> DecodeAsync<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken)

2
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.InitIptcProfile(); this.InitIptcProfile();
this.InitDerivedMetadataProperties(); this.InitDerivedMetadataProperties();
return new Image<TPixel>(this.Configuration, spectralConverter.PixelBuffer, this.Metadata); return new Image<TPixel>(this.Configuration, spectralConverter.GetPixelBuffer(), this.Metadata);
} }
/// <inheritdoc/> /// <inheritdoc/>

2
src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs

@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
scanDecoder.ResetInterval = 0; scanDecoder.ResetInterval = 0;
jpegDecoder.ParseStream(stream, scanDecoder, CancellationToken.None); jpegDecoder.ParseStream(stream, scanDecoder, CancellationToken.None);
CopyImageBytesToBuffer(buffer, spectralConverter.PixelBuffer); CopyImageBytesToBuffer(buffer, spectralConverter.GetPixelBuffer());
} }
else else
{ {

30
src/ImageSharp/Memory/Buffer2D{T}.cs

@ -116,6 +116,36 @@ namespace SixLabors.ImageSharp.Memory
: this.GetRowMemorySlow(y).Span; : this.GetRowMemorySlow(y).Span;
} }
internal bool TryGetPaddedRowSpan(int y, int padding, out Span<T> paddedSpan)
{
DebugGuard.MustBeGreaterThanOrEqualTo(y, 0, nameof(y));
DebugGuard.MustBeLessThan(y, this.Height, nameof(y));
int stride = this.Width + padding;
if (this.cachedMemory.Length > 0)
{
paddedSpan = this.cachedMemory.Span.Slice(y * this.Width);
if (paddedSpan.Length < stride)
{
return false;
}
paddedSpan = paddedSpan.Slice(0, stride);
return true;
}
Memory<T> memory = this.FastMemoryGroup.GetRemainingSliceOfBuffer(y * (long)this.Width);
if (memory.Length < stride)
{
paddedSpan = default;
return false;
}
paddedSpan = memory.Span.Slice(0, stride);
return true;
}
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
internal ref T GetElementUnsafe(int x, int y) internal ref T GetElementUnsafe(int x, int y)
{ {

33
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs

@ -39,12 +39,8 @@ namespace SixLabors.ImageSharp.Memory
int bufferIdx = (int)(start / group.BufferLength); int bufferIdx = (int)(start / group.BufferLength);
if (bufferIdx < 0) // if (bufferIdx < 0 || bufferIdx >= group.Count)
{ if ((uint)bufferIdx >= group.Count)
throw new ArgumentOutOfRangeException(nameof(start));
}
if (bufferIdx >= group.Count)
{ {
throw new ArgumentOutOfRangeException(nameof(start)); throw new ArgumentOutOfRangeException(nameof(start));
} }
@ -61,6 +57,31 @@ namespace SixLabors.ImageSharp.Memory
return memory.Slice(bufferStart, length); return memory.Slice(bufferStart, length);
} }
/// <summary>
/// Returns the slice of the buffer starting at global index <paramref name="start"/> that goes until the end of the buffer.
/// </summary>
internal static Memory<T> GetRemainingSliceOfBuffer<T>(this IMemoryGroup<T> group, long start)
where T : struct
{
Guard.NotNull(group, nameof(group));
Guard.IsTrue(group.IsValid, nameof(group), "Group must be valid!");
Guard.MustBeLessThan(start, group.TotalLength, nameof(start));
int bufferIdx = (int)(start / group.BufferLength);
// if (bufferIdx < 0 || bufferIdx >= group.Count)
if ((uint)bufferIdx >= group.Count)
{
throw new ArgumentOutOfRangeException(nameof(start));
}
int bufferStart = (int)(start % group.BufferLength);
Memory<T> memory = group[bufferIdx];
return memory.Slice(bufferStart);
}
internal static void CopyTo<T>(this IMemoryGroup<T> source, Span<T> target) internal static void CopyTo<T>(this IMemoryGroup<T> source, Span<T> target)
where T : struct where T : struct
{ {

4
src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs

@ -32,9 +32,7 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
int count = redChannel.Length; int count = redChannel.Length;
Guard.IsTrue(greenChannel.Length == count, nameof(greenChannel), "Channels must be of same size!"); GuardPackFromRgbPlanes(greenChannel, blueChannel, destination, count);
Guard.IsTrue(blueChannel.Length == count, nameof(blueChannel), "Channels must be of same size!");
Guard.IsTrue(destination.Length > count + 2, nameof(destination), "'destination' must contain a padding of 3 elements!");
SimdUtils.PackFromRgbPlanes(configuration, redChannel, greenChannel, blueChannel, destination); SimdUtils.PackFromRgbPlanes(configuration, redChannel, greenChannel, blueChannel, destination);
} }

4
src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs

@ -67,9 +67,7 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
int count = redChannel.Length; int count = redChannel.Length;
Guard.IsTrue(greenChannel.Length == count, nameof(greenChannel), "Channels must be of same size!"); GuardPackFromRgbPlanes(greenChannel, blueChannel, destination, count);
Guard.IsTrue(blueChannel.Length == count, nameof(blueChannel), "Channels must be of same size!");
Guard.IsTrue(destination.Length > count, nameof(destination), "'destination' span should not be shorter than the source channels!");
SimdUtils.PackFromRgbPlanes(configuration, redChannel, greenChannel, blueChannel, destination); SimdUtils.PackFromRgbPlanes(configuration, redChannel, greenChannel, blueChannel, destination);
} }

14
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

@ -181,11 +181,7 @@ namespace SixLabors.ImageSharp.PixelFormats
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
int count = redChannel.Length; int count = redChannel.Length;
Guard.IsTrue(greenChannel.Length == count, nameof(greenChannel), "Channels must be of same size!"); GuardPackFromRgbPlanes(greenChannel, blueChannel, destination, count);
Guard.IsTrue(blueChannel.Length == count, nameof(blueChannel), "Channels must be of same size!");
Guard.IsTrue(destination.Length > count + 2, nameof(destination), "'destination' must contain a padding of 3 elements!");
Guard.DestinationShouldNotBeTooShort(redChannel, destination, nameof(destination));
Rgb24 rgb24 = default; Rgb24 rgb24 = default;
ref byte r = ref MemoryMarshal.GetReference(redChannel); ref byte r = ref MemoryMarshal.GetReference(redChannel);
@ -201,5 +197,13 @@ namespace SixLabors.ImageSharp.PixelFormats
Unsafe.Add(ref d, i).FromRgb24(rgb24); Unsafe.Add(ref d, i).FromRgb24(rgb24);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)]
internal static void GuardPackFromRgbPlanes(ReadOnlySpan<byte> greenChannel, ReadOnlySpan<byte> blueChannel, Span<TPixel> destination, int count)
{
Guard.IsTrue(greenChannel.Length == count, nameof(greenChannel), "Channels must be of same size!");
Guard.IsTrue(blueChannel.Length == count, nameof(blueChannel), "Channels must be of same size!");
Guard.IsTrue(destination.Length > count + 2, nameof(destination), "'destination' must contain a padding of 3 elements!");
}
} }
} }

4
src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
/// <summary> /// <summary>
/// Apply modifiers used requested by ToVector4() conversion. /// Apply modifiers used requested by ToVector4() conversion.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
internal static void ApplyForwardConversionModifiers(Span<Vector4> vectors, PixelConversionModifiers modifiers) internal static void ApplyForwardConversionModifiers(Span<Vector4> vectors, PixelConversionModifiers modifiers)
{ {
if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand))
@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
/// <summary> /// <summary>
/// Apply modifiers used requested by FromVector4() conversion. /// Apply modifiers used requested by FromVector4() conversion.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(InliningOptions.ShortMethod)]
internal static void ApplyBackwardConversionModifiers(Span<Vector4> vectors, PixelConversionModifiers modifiers) internal static void ApplyBackwardConversionModifiers(Span<Vector4> vectors, PixelConversionModifiers modifiers)
{ {
if (modifiers.IsDefined(PixelConversionModifiers.Premultiply)) if (modifiers.IsDefined(PixelConversionModifiers.Premultiply))

6
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -187,6 +188,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
bool compand, bool compand,
bool premultiplyAlpha) bool premultiplyAlpha)
{ {
PixelAlphaRepresentation? alphaRepresentation = PixelOperations<TPixel>.Instance.GetPixelTypeInfo()?.AlphaRepresentation;
// Premultiply only if alpha representation is unknown or Unassociated:
bool needsPremultiplication = alphaRepresentation == null || alphaRepresentation.Value == PixelAlphaRepresentation.Unassociated;
premultiplyAlpha &= needsPremultiplication;
PixelConversionModifiers conversionModifiers = GetModifiers(compand, premultiplyAlpha); PixelConversionModifiers conversionModifiers = GetModifiers(compand, premultiplyAlpha);
Buffer2DRegion<TPixel> sourceRegion = source.PixelBuffer.GetRegion(sourceRectangle); Buffer2DRegion<TPixel> sourceRegion = source.PixelBuffer.GetRegion(sourceRectangle);

6
tests/ImageSharp.Benchmarks/Codecs/Jpeg/CmykColorConversion.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromCmykBasic(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromCmykBasic(8).ConvertToRgbInplace(values);
} }
[Benchmark] [Benchmark]
@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromCmykVector8(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromCmykVector8(8).ConvertToRgbInplace(values);
} }
[Benchmark] [Benchmark]
@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromCmykAvx2(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromCmykAvx2(8).ConvertToRgbInplace(values);
} }
} }
} }

3
tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversionBenchmark.cs

@ -19,13 +19,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
protected Buffer2D<float>[] Input { get; private set; } protected Buffer2D<float>[] Input { get; private set; }
protected Vector4[] Output { get; private set; }
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
this.Input = CreateRandomValues(this.componentCount, Count); this.Input = CreateRandomValues(this.componentCount, Count);
this.Output = new Vector4[Count];
} }
[GlobalCleanup] [GlobalCleanup]

10
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs

@ -44,8 +44,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
} }
} }
[Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")] [Benchmark(Baseline = true)]
public SDSize JpegSystemDrawing() public SDSize SystemDrawing()
{ {
using (var memoryStream = new MemoryStream(this.jpegBytes)) using (var memoryStream = new MemoryStream(this.jpegBytes))
{ {
@ -56,12 +56,12 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
} }
} }
[Benchmark(Description = "Decode Jpeg - ImageSharp")] [Benchmark]
public Size JpegImageSharp() public Size ImageSharp()
{ {
using (var memoryStream = new MemoryStream(this.jpegBytes)) using (var memoryStream = new MemoryStream(this.jpegBytes))
{ {
using (var image = Image.Load<Rgba32>(memoryStream, new JpegDecoder { IgnoreMetadata = true })) using (var image = Image.Load(memoryStream, new JpegDecoder { IgnoreMetadata = true }))
{ {
return new Size(image.Width, image.Height); return new Size(image.Width, image.Height);
} }

4
tests/ImageSharp.Benchmarks/Codecs/Jpeg/GrayscaleColorConversion.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromGrayscaleBasic(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromGrayscaleBasic(8).ConvertToRgbInplace(values);
} }
[Benchmark] [Benchmark]
@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromGrayscaleAvx2(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromGrayscaleAvx2(8).ConvertToRgbInplace(values);
} }
} }
} }

6
tests/ImageSharp.Benchmarks/Codecs/Jpeg/RgbColorConversion.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromRgbBasic(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromRgbBasic(8).ConvertToRgbInplace(values);
} }
[Benchmark] [Benchmark]
@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromRgbVector8(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromRgbVector8(8).ConvertToRgbInplace(values);
} }
[Benchmark] [Benchmark]
@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromRgbAvx2(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromRgbAvx2(8).ConvertToRgbInplace(values);
} }
} }
} }

8
tests/ImageSharp.Benchmarks/Codecs/Jpeg/YCbCrColorConversion.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYCbCrBasic(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromYCbCrBasic(8).ConvertToRgbInplace(values);
} }
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYCbCrVector4(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromYCbCrVector4(8).ConvertToRgbInplace(values);
} }
[Benchmark] [Benchmark]
@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYCbCrVector8(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromYCbCrVector8(8).ConvertToRgbInplace(values);
} }
[Benchmark] [Benchmark]
@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYCbCrAvx2(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromYCbCrAvx2(8).ConvertToRgbInplace(values);
} }
} }
} }

6
tests/ImageSharp.Benchmarks/Codecs/Jpeg/YccKColorConverter.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYccKBasic(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromYccKBasic(8).ConvertToRgbInplace(values);
} }
[Benchmark] [Benchmark]
@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYccKVector8(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromYccKVector8(8).ConvertToRgbInplace(values);
} }
[Benchmark] [Benchmark]
@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{ {
var values = new JpegColorConverter.ComponentValues(this.Input, 0); var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYccKAvx2(8).ConvertToRgba(values, this.Output); new JpegColorConverter.FromYccKAvx2(8).ConvertToRgbInplace(values);
} }
} }
} }

2
tests/ImageSharp.Benchmarks/Config.cs

@ -33,7 +33,6 @@ namespace SixLabors.ImageSharp.Benchmarks
{ {
public MultiFramework() => this.AddJob( public MultiFramework() => this.AddJob(
Job.Default.WithRuntime(ClrRuntime.Net472), Job.Default.WithRuntime(ClrRuntime.Net472),
Job.Default.WithRuntime(CoreRuntime.Core21),
Job.Default.WithRuntime(CoreRuntime.Core31)); Job.Default.WithRuntime(CoreRuntime.Core31));
} }
@ -41,7 +40,6 @@ namespace SixLabors.ImageSharp.Benchmarks
{ {
public ShortMultiFramework() => this.AddJob( public ShortMultiFramework() => this.AddJob(
Job.Default.WithRuntime(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3), Job.Default.WithRuntime(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3),
Job.Default.WithRuntime(CoreRuntime.Core21).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3),
Job.Default.WithRuntime(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3)); Job.Default.WithRuntime(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3));
} }

3
tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs

@ -31,7 +31,6 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave
public class LoadResizeSaveStressRunner public class LoadResizeSaveStressRunner
{ {
private const int ThumbnailSize = 150;
private const int Quality = 75; private const int Quality = 75;
private const string ImageSharp = nameof(ImageSharp); private const string ImageSharp = nameof(ImageSharp);
private const string SystemDrawing = nameof(SystemDrawing); private const string SystemDrawing = nameof(SystemDrawing);
@ -58,6 +57,8 @@ namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave
public JpegKind Filter { get; set; } public JpegKind Filter { get; set; }
public int ThumbnailSize { get; set; } = 150;
private static readonly string[] ProgressiveFiles = private static readonly string[] ProgressiveFiles =
{ {
"ancyloscelis-apiformis-m-paraguay-face_2014-08-08-095255-zs-pmax_15046500892_o.jpg", "ancyloscelis-apiformis-m-paraguay-face_2014-08-08-095255-zs-pmax_15046500892_o.jpg",

31
tests/ImageSharp.Benchmarks/Processing/Resize.cs

@ -215,4 +215,35 @@ namespace SixLabors.ImageSharp.Benchmarks
// SystemDrawing | Core | Core | 3032 | 400 | 118.3 ms | 6.899 ms | 0.3781 ms | 1.00 | 0.00 | - | - | - | 96 B | // SystemDrawing | Core | Core | 3032 | 400 | 118.3 ms | 6.899 ms | 0.3781 ms | 1.00 | 0.00 | - | - | - | 96 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 122.4 ms | 15.069 ms | 0.8260 ms | 1.03 | 0.01 | - | - | - | 15712 B | // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | Core | 3032 | 400 | 122.4 ms | 15.069 ms | 0.8260 ms | 1.03 | 0.01 | - | - | - | 15712 B |
} }
public class Resize_Bicubic_Compare_Rgba32_Rgb24
{
private Resize_Bicubic_Rgb24 rgb24;
private Resize_Bicubic_Rgba32 rgba32;
[GlobalSetup]
public void Setup()
{
this.rgb24 = new Resize_Bicubic_Rgb24();
this.rgb24.Setup();
this.rgba32 = new Resize_Bicubic_Rgba32();
this.rgba32.Setup();
}
[GlobalCleanup]
public void Cleanup()
{
this.rgb24.Cleanup();
this.rgba32.Cleanup();
}
[Benchmark]
public void SystemDrawing() => this.rgba32.SystemDrawing();
[Benchmark(Baseline = true)]
public void Rgba32() => this.rgba32.ImageSharp_P1();
[Benchmark]
public void Rgb24() => this.rgb24.ImageSharp_P1();
}
} }

3
tests/ImageSharp.Tests.ProfilingSandbox/LoadResizeSaveParallelMemoryStress.cs

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox
this.benchmarks = new LoadResizeSaveStressRunner() this.benchmarks = new LoadResizeSaveStressRunner()
{ {
// MaxDegreeOfParallelism = 10, // MaxDegreeOfParallelism = 10,
// Filter = JpegKind.Baseline Filter = JpegKind.Baseline,
}; };
this.benchmarks.Init(); this.benchmarks.Init();
} }
@ -58,6 +58,7 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox
lrs.SystemDrawingBenchmarkParallel(); lrs.SystemDrawingBenchmarkParallel();
break; break;
case ConsoleKey.D2: case ConsoleKey.D2:
Console.WriteLine($"Images: {lrs.benchmarks.Images.Length}");
lrs.ImageSharpBenchmarkParallel(); lrs.ImageSharpBenchmarkParallel();
break; break;
case ConsoleKey.D3: case ConsoleKey.D3:

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

@ -355,13 +355,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrVector4(8) : new JpegColorConverter.FromYCbCrBasic(8); JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrVector4(8) : new JpegColorConverter.FromYCbCrBasic(8);
// Warm up: // Warm up:
converter.ConvertToRgba(values, result); converter.ConvertToRgbInplace(values);
using (new MeasureGuard(this.Output, $"{converter.GetType().Name} x {times}")) using (new MeasureGuard(this.Output, $"{converter.GetType().Name} x {times}"))
{ {
for (int i = 0; i < times; i++) for (int i = 0; i < times; i++)
{ {
converter.ConvertToRgba(values, result); converter.ConvertToRgbInplace(values);
} }
} }
} }
@ -374,6 +374,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
float maxVal = 255f) float maxVal = 255f)
{ {
var rnd = new Random(seed); var rnd = new Random(seed);
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++)
{ {
@ -415,39 +416,48 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
int resultBufferLength, int resultBufferLength,
int seed) int seed)
{ {
JpegColorConverter.ComponentValues values = CreateRandomValues(componentCount, inputBufferLength, seed); JpegColorConverter.ComponentValues original = CreateRandomValues(componentCount, inputBufferLength, seed);
var result = new Vector4[resultBufferLength]; JpegColorConverter.ComponentValues values = Copy(original);
converter.ConvertToRgba(values, result); converter.ConvertToRgbInplace(values);
for (int i = 0; i < resultBufferLength; i++) for (int i = 0; i < resultBufferLength; i++)
{ {
Validate(converter.ColorSpace, values, result, i); Validate(converter.ColorSpace, original, values, i);
}
static JpegColorConverter.ComponentValues Copy(JpegColorConverter.ComponentValues values)
{
Span<float> c0 = values.Component0.ToArray();
Span<float> c1 = values.ComponentCount > 1 ? values.Component1.ToArray().AsSpan() : c0;
Span<float> c2 = values.ComponentCount > 2 ? values.Component2.ToArray().AsSpan() : c0;
Span<float> c3 = values.ComponentCount > 3 ? values.Component3.ToArray().AsSpan() : Span<float>.Empty;
return new JpegColorConverter.ComponentValues(values.ComponentCount, c0, c1, c2, c3);
} }
} }
private static void Validate( private static void Validate(
JpegColorSpace colorSpace, JpegColorSpace colorSpace,
in JpegColorConverter.ComponentValues values, in JpegColorConverter.ComponentValues original,
Vector4[] result, in JpegColorConverter.ComponentValues result,
int i) int i)
{ {
switch (colorSpace) switch (colorSpace)
{ {
case JpegColorSpace.Grayscale: case JpegColorSpace.Grayscale:
ValidateGrayScale(values, result, i); ValidateGrayScale(original, result, i);
break; break;
case JpegColorSpace.Ycck: case JpegColorSpace.Ycck:
ValidateCyyK(values, result, i); ValidateCyyK(original, result, i);
break; break;
case JpegColorSpace.Cmyk: case JpegColorSpace.Cmyk:
ValidateCmyk(values, result, i); ValidateCmyk(original, result, i);
break; break;
case JpegColorSpace.RGB: case JpegColorSpace.RGB:
ValidateRgb(values, result, i); ValidateRgb(original, result, i);
break; break;
case JpegColorSpace.YCbCr: case JpegColorSpace.YCbCr:
ValidateYCbCr(values, result, i); ValidateYCbCr(original, result, i);
break; break;
default: default:
Assert.True(false, $"Colorspace {colorSpace} not supported!"); Assert.True(false, $"Colorspace {colorSpace} not supported!");
@ -455,22 +465,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
} }
} }
private static void ValidateYCbCr(in JpegColorConverter.ComponentValues values, Vector4[] result, int i) private static void ValidateYCbCr(in JpegColorConverter.ComponentValues values, in JpegColorConverter.ComponentValues result, int i)
{ {
float y = values.Component0[i]; float y = values.Component0[i];
float cb = values.Component1[i]; float cb = values.Component1[i];
float cr = values.Component2[i]; float cr = values.Component2[i];
var ycbcr = new YCbCr(y, cb, cr); var ycbcr = new YCbCr(y, cb, cr);
Vector4 rgba = result[i]; var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]);
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z);
var expected = ColorSpaceConverter.ToRgb(ycbcr); var expected = ColorSpaceConverter.ToRgb(ycbcr);
Assert.Equal(expected, actual, ColorSpaceComparer); Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
} }
private static void ValidateCyyK(in JpegColorConverter.ComponentValues values, Vector4[] result, int i) private static void ValidateCyyK(in JpegColorConverter.ComponentValues values, in JpegColorConverter.ComponentValues result, int i)
{ {
var v = new Vector4(0, 0, 0, 1F); var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
@ -489,39 +497,34 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
v *= scale; v *= scale;
Vector4 rgba = result[i]; var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]);
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z);
var expected = new Rgb(v.X, v.Y, v.Z); var expected = new Rgb(v.X, v.Y, v.Z);
Assert.Equal(expected, actual, ColorSpaceComparer); Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
} }
private static void ValidateRgb(in JpegColorConverter.ComponentValues values, Vector4[] result, int i) private static void ValidateRgb(in JpegColorConverter.ComponentValues values, in JpegColorConverter.ComponentValues result, int i)
{ {
float r = values.Component0[i]; float r = values.Component0[i];
float g = values.Component1[i]; float g = values.Component1[i];
float b = values.Component2[i]; float b = values.Component2[i];
Vector4 rgba = result[i];
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]);
var expected = new Rgb(r / 255F, g / 255F, b / 255F); var expected = new Rgb(r / 255F, g / 255F, b / 255F);
Assert.Equal(expected, actual, ColorSpaceComparer); Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
} }
private static void ValidateGrayScale(in JpegColorConverter.ComponentValues values, Vector4[] result, int i) private static void ValidateGrayScale(in JpegColorConverter.ComponentValues values, in JpegColorConverter.ComponentValues result, int i)
{ {
float y = values.Component0[i]; float y = values.Component0[i];
Vector4 rgba = result[i]; var actual = new Rgb(result.Component0[i], result.Component0[i], result.Component0[i]);
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z);
var expected = new Rgb(y / 255F, y / 255F, y / 255F); var expected = new Rgb(y / 255F, y / 255F, y / 255F);
Assert.Equal(expected, actual, ColorSpaceComparer); Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
} }
private static void ValidateCmyk(in JpegColorConverter.ComponentValues values, Vector4[] result, int i) private static void ValidateCmyk(in JpegColorConverter.ComponentValues values, in JpegColorConverter.ComponentValues result, int i)
{ {
var v = new Vector4(0, 0, 0, 1F); var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
@ -538,12 +541,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
v *= scale; v *= scale;
Vector4 rgba = result[i]; var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]);
var actual = new Rgb(rgba.X, rgba.Y, rgba.Z);
var expected = new Rgb(v.X, v.Y, v.Z); var expected = new Rgb(v.X, v.Y, v.Z);
Assert.Equal(expected, actual, ColorSpaceComparer); Assert.Equal(expected, actual, ColorSpaceComparer);
Assert.Equal(1, rgba.W);
} }
} }
} }

8
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs

@ -13,9 +13,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public partial class JpegDecoderTests public partial class JpegDecoderTests
{ {
[Theory] [Theory]
[WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32, false)] [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgb24, false)]
[WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgba32, true)] [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, true)]
[WithFile(TestImages.Jpeg.Baseline.Turtle420, PixelTypes.Rgba32, true)] [WithFile(TestImages.Jpeg.Baseline.Turtle420, PixelTypes.Rgb24, true)]
public void DecodeBaselineJpeg<TPixel>(TestImageProvider<TPixel> provider, bool enforceDiscontiguousBuffers) public void DecodeBaselineJpeg<TPixel>(TestImageProvider<TPixel> provider, bool enforceDiscontiguousBuffers)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
if (!string.IsNullOrEmpty(nonContiguousBuffersStr)) if (!string.IsNullOrEmpty(nonContiguousBuffersStr))
{ {
provider.LimitAllocatorBufferCapacity().InPixels(1000 * 8); provider.LimitAllocatorBufferCapacity().InPixels(16_000);
} }
using Image<TPixel> image = provider.GetImage(JpegDecoder); using Image<TPixel> image = provider.GetImage(JpegDecoder);

14
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs

@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
using (var stream = new MemoryStream(testFile.Bytes, false)) using (var stream = new MemoryStream(testFile.Bytes, false))
{ {
var decoder = new JpegDecoder(); var decoder = new JpegDecoder();
using (Image<Rgba32> image = decoder.Decode<Rgba32>(Configuration.Default, stream)) using (Image image = decoder.Decode(Configuration.Default, stream))
{ {
ImageMetadata meta = image.Metadata; ImageMetadata meta = image.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution); Assert.Equal(xResolution, meta.HorizontalResolution);
@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false)) using (var stream = new MemoryStream(testFile.Bytes, false))
{ {
using (Image<Rgba32> image = JpegDecoder.Decode<Rgba32>(Configuration.Default, stream)) using (Image image = JpegDecoder.Decode(Configuration.Default, stream))
{ {
JpegMetadata meta = image.Metadata.GetJpegMetadata(); JpegMetadata meta = image.Metadata.GetJpegMetadata();
Assert.Equal(quality, meta.Quality); Assert.Equal(quality, meta.Quality);
@ -158,11 +158,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
} }
[Theory] [Theory]
[WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgba32, JpegColorType.Luminance)] [WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgb24, JpegColorType.Luminance)]
[WithFile(TestImages.Jpeg.Baseline.Jpeg420Small, PixelTypes.Rgba32, JpegColorType.YCbCrRatio420)] [WithFile(TestImages.Jpeg.Baseline.Jpeg420Small, PixelTypes.Rgb24, JpegColorType.YCbCrRatio420)]
[WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgba32, JpegColorType.YCbCrRatio444)] [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgb24, JpegColorType.YCbCrRatio444)]
[WithFile(TestImages.Jpeg.Baseline.JpegRgb, PixelTypes.Rgba32, JpegColorType.Rgb)] [WithFile(TestImages.Jpeg.Baseline.JpegRgb, PixelTypes.Rgb24, JpegColorType.Rgb)]
[WithFile(TestImages.Jpeg.Baseline.Cmyk, PixelTypes.Rgba32, JpegColorType.Cmyk)] [WithFile(TestImages.Jpeg.Baseline.Cmyk, PixelTypes.Rgb24, JpegColorType.Cmyk)]
public void Decode_DetectsCorrectColorType<TPixel>(TestImageProvider<TPixel> provider, JpegColorType expectedColorType) public void Decode_DetectsCorrectColorType<TPixel>(TestImageProvider<TPixel> provider, JpegColorType expectedColorType)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {

12
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public const string DecodeProgressiveJpegOutputName = "DecodeProgressiveJpeg"; public const string DecodeProgressiveJpegOutputName = "DecodeProgressiveJpeg";
[Theory] [Theory]
[WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)] [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgb24)]
public void DecodeProgressiveJpeg<TPixel>(TestImageProvider<TPixel> provider) public void DecodeProgressiveJpeg<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -30,17 +30,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
} }
[Theory] [Theory]
[WithFile(TestImages.Jpeg.Progressive.Progress, PixelTypes.Rgba32)] [WithFile(TestImages.Jpeg.Progressive.Progress, PixelTypes.Rgb24)]
public void DecodeProgressiveJpeg_WithLimitedAllocatorBufferCapacity(TestImageProvider<Rgba32> provider) public void DecodeProgressiveJpeg_WithLimitedAllocatorBufferCapacity(TestImageProvider<Rgb24> provider)
{ {
static void RunTest(string providerDump, string nonContiguousBuffersStr) static void RunTest(string providerDump, string nonContiguousBuffersStr)
{ {
TestImageProvider<Rgba32> provider = TestImageProvider<Rgb24> provider =
BasicSerializer.Deserialize<TestImageProvider<Rgba32>>(providerDump); BasicSerializer.Deserialize<TestImageProvider<Rgb24>>(providerDump);
provider.LimitAllocatorBufferCapacity().InBytesSqrt(200); provider.LimitAllocatorBufferCapacity().InBytesSqrt(200);
using Image<Rgba32> image = provider.GetImage(JpegDecoder); using Image<Rgb24> image = provider.GetImage(JpegDecoder);
image.DebugSave(provider, nonContiguousBuffersStr); image.DebugSave(provider, nonContiguousBuffersStr);
provider.Utility.TestName = DecodeProgressiveJpegOutputName; provider.Utility.TestName = DecodeProgressiveJpegOutputName;

10
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Trait("Format", "Jpg")] [Trait("Format", "Jpg")]
public partial class JpegDecoderTests public partial class JpegDecoderTests
{ {
public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.RgbaVector; public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.Bgr24 | PixelTypes.RgbaVector;
private const float BaselineTolerance = 0.001F / 100; private const float BaselineTolerance = 0.001F / 100;
@ -85,6 +85,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public const string DecodeBaselineJpegOutputName = "DecodeBaselineJpeg"; public const string DecodeBaselineJpegOutputName = "DecodeBaselineJpeg";
[Fact]
public void Decode_NonGeneric_CreatesRgb24Image()
{
string file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small);
using var image = Image.Load(file);
Assert.IsType<Image<Rgb24>>(image);
}
[Theory] [Theory]
[WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes)] [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes)]
public void JpegDecoder_IsNotBoundToSinglePixelType<TPixel>(TestImageProvider<TPixel> provider) public void JpegDecoder_IsNotBoundToSinglePixelType<TPixel>(TestImageProvider<TPixel> provider)

2
tests/ImageSharp.Tests/Formats/Jpg/SpectralToPixelConversionTests.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
provider.Utility.TestName = JpegDecoderTests.DecodeBaselineJpegOutputName; provider.Utility.TestName = JpegDecoderTests.DecodeBaselineJpegOutputName;
// Comparison // Comparison
using (Image<TPixel> image = new Image<TPixel>(Configuration.Default, converter.PixelBuffer, new ImageMetadata())) using (Image<TPixel> image = new Image<TPixel>(Configuration.Default, converter.GetPixelBuffer(), new ImageMetadata()))
using (Image<TPixel> referenceImage = provider.GetReferenceOutputImage<TPixel>(appendPixelTypeToFileName: false)) using (Image<TPixel> referenceImage = provider.GetReferenceOutputImage<TPixel>(appendPixelTypeToFileName: false))
{ {
ImageSimilarityReport report = ImageComparer.Exact.CompareImagesOrFrames(referenceImage, image); ImageSimilarityReport report = ImageComparer.Exact.CompareImagesOrFrames(referenceImage, image);

26
tests/ImageSharp.Tests/Memory/Buffer2DTests.cs

@ -117,6 +117,32 @@ namespace SixLabors.ImageSharp.Tests.Memory
} }
} }
[Theory]
[InlineData(10, 0, 0, 0)]
[InlineData(10, 0, 2, 0)]
[InlineData(10, 1, 2, 0)]
[InlineData(10, 1, 3, 0)]
[InlineData(10, 1, 5, -1)]
[InlineData(10, 2, 2, -1)]
[InlineData(10, 3, 2, 1)]
[InlineData(10, 4, 2, -1)]
[InlineData(30, 3, 2, 0)]
[InlineData(30, 4, 1, -1)]
public void TryGetPaddedRowSpanY(int bufferCapacity, int y, int padding, int expectedBufferIndex)
{
this.MemoryAllocator.BufferCapacityInBytes = bufferCapacity;
using Buffer2D<byte> buffer = this.MemoryAllocator.Allocate2D<byte>(3, 5);
bool expectSuccess = expectedBufferIndex >= 0;
bool success = buffer.TryGetPaddedRowSpan(y, padding, out Span<byte> paddedSpan);
Xunit.Assert.Equal(expectSuccess, success);
if (success)
{
int expectedSubBufferOffset = (3 * y) - (expectedBufferIndex * buffer.FastMemoryGroup.BufferLength);
Assert.SpanPointsTo(paddedSpan, buffer.FastMemoryGroup[expectedBufferIndex], expectedSubBufferOffset);
}
}
public static TheoryData<int, int, int, int> GetRowSpanY_OutOfRange_Data = new TheoryData<int, int, int, int>() public static TheoryData<int, int, int, int> GetRowSpanY_OutOfRange_Data = new TheoryData<int, int, int, int>()
{ {
{ Big, 10, 8, -1 }, { Big, 10, 8, -1 },

102
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

@ -19,9 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public class ResizeTests public class ResizeTests
{ {
private const PixelTypes CommonNonDefaultPixelTypes = private const PixelTypes CommonNonDefaultPixelTypes =
PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Bgr24 | PixelTypes.RgbaVector;
private const PixelTypes DefaultPixelType = PixelTypes.Rgba32;
public static readonly string[] AllResamplerNames = TestUtils.GetAllResamplerNames(); public static readonly string[] AllResamplerNames = TestUtils.GetAllResamplerNames();
@ -188,7 +186,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
[Theory] [Theory]
[WithTestPatternImages(100, 100, DefaultPixelType)] [WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
public void Resize_Compand<TPixel>(TestImageProvider<TPixel> provider) public void Resize_Compand<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -202,8 +200,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
[Theory] [Theory]
[WithFile(TestImages.Png.Kaboom, DefaultPixelType, false)] [WithFile(TestImages.Png.Kaboom, PixelTypes.Rgba32, false)]
[WithFile(TestImages.Png.Kaboom, DefaultPixelType, true)] [WithFile(TestImages.Png.Kaboom, PixelTypes.Rgba32, true)]
public void Resize_DoesNotBleedAlphaPixels<TPixel>(TestImageProvider<TPixel> provider, bool compand) public void Resize_DoesNotBleedAlphaPixels<TPixel>(TestImageProvider<TPixel> provider, bool compand)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -217,8 +215,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
[Theory] [Theory]
[WithFile(TestImages.Png.Kaboom, DefaultPixelType, false)] [WithFile(TestImages.Png.Kaboom, PixelTypes.Rgba32, false)]
[WithFile(TestImages.Png.Kaboom, DefaultPixelType, true)] [WithFile(TestImages.Png.Kaboom, PixelTypes.Rgba32, true)]
public void Resize_PremultiplyAlpha<TPixel>(TestImageProvider<TPixel> provider, bool premultiplyAlpha) public void Resize_PremultiplyAlpha<TPixel>(TestImageProvider<TPixel> provider, bool premultiplyAlpha)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -243,7 +241,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
[Theory] [Theory]
[WithFile(TestImages.Gif.Giphy, DefaultPixelType)] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)]
public void Resize_IsAppliedToAllFrames<TPixel>(TestImageProvider<TPixel> provider) public void Resize_IsAppliedToAllFrames<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -265,7 +263,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)]
public void Resize_ThrowsForWrappedMemoryImage<TPixel>(TestImageProvider<TPixel> provider) public void Resize_ThrowsForWrappedMemoryImage<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -283,10 +281,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 1)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24, 1)]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 4)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24, 4)]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 8)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24, 8)]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType, -1)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24, -1)]
public void Resize_WorksWithAllParallelismLevels<TPixel>( public void Resize_WorksWithAllParallelismLevels<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
int maxDegreeOfParallelism) int maxDegreeOfParallelism)
@ -305,27 +303,27 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), nameof(AllResamplerNames), DefaultPixelType, 0.5f, null, null)] [WithFileCollection(nameof(CommonTestImages), nameof(AllResamplerNames), PixelTypes.Rgba32 | PixelTypes.Rgb24, 0.5f, null, null)]
[WithFileCollection( [WithFileCollection(
nameof(CommonTestImages), nameof(CommonTestImages),
nameof(SmokeTestResamplerNames), nameof(SmokeTestResamplerNames),
DefaultPixelType, PixelTypes.Rgba32 | PixelTypes.Rgb24,
0.3f, 0.3f,
null, null,
null)] null)]
[WithFileCollection( [WithFileCollection(
nameof(CommonTestImages), nameof(CommonTestImages),
nameof(SmokeTestResamplerNames), nameof(SmokeTestResamplerNames),
DefaultPixelType, PixelTypes.Rgba32 | PixelTypes.Rgb24,
1.8f, 1.8f,
null, null,
null)] null)]
[WithTestPatternImages(nameof(SmokeTestResamplerNames), 100, 100, DefaultPixelType, 0.5f, null, null)] [WithTestPatternImages(nameof(SmokeTestResamplerNames), 100, 100, PixelTypes.Rgba32, 0.5f, null, null)]
[WithTestPatternImages(nameof(SmokeTestResamplerNames), 100, 100, DefaultPixelType, 1f, null, null)] [WithTestPatternImages(nameof(SmokeTestResamplerNames), 100, 100, PixelTypes.Rgba32, 1f, null, null)]
[WithTestPatternImages(nameof(SmokeTestResamplerNames), 50, 50, DefaultPixelType, 8f, null, null)] [WithTestPatternImages(nameof(SmokeTestResamplerNames), 50, 50, PixelTypes.Rgba32, 8f, null, null)]
[WithTestPatternImages(nameof(SmokeTestResamplerNames), 201, 199, DefaultPixelType, null, 100, 99)] [WithTestPatternImages(nameof(SmokeTestResamplerNames), 201, 199, PixelTypes.Rgba32, null, 100, 99)]
[WithTestPatternImages(nameof(SmokeTestResamplerNames), 301, 1180, DefaultPixelType, null, 300, 480)] [WithTestPatternImages(nameof(SmokeTestResamplerNames), 301, 1180, PixelTypes.Rgba32, null, 300, 480)]
[WithTestPatternImages(nameof(SmokeTestResamplerNames), 49, 80, DefaultPixelType, null, 301, 100)] [WithTestPatternImages(nameof(SmokeTestResamplerNames), 49, 80, PixelTypes.Rgba32, null, 301, 100)]
public void Resize_WorksWithAllResamplers<TPixel>( public void Resize_WorksWithAllResamplers<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
string samplerName, string samplerName,
@ -382,7 +380,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ResizeFromSourceRectangle<TPixel>(TestImageProvider<TPixel> provider) public void ResizeFromSourceRectangle<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -405,12 +403,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
false)); false));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider, appendPixelTypeToFileName: false);
} }
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ResizeHeightAndKeepAspect<TPixel>(TestImageProvider<TPixel> provider) public void ResizeHeightAndKeepAspect<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -419,12 +417,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
image.Mutate(x => x.Resize(0, image.Height / 3, false)); image.Mutate(x => x.Resize(0, image.Height / 3, false));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider, appendPixelTypeToFileName: false);
} }
} }
[Theory] [Theory]
[WithTestPatternImages(10, 100, DefaultPixelType)] [WithTestPatternImages(10, 100, PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ResizeHeightCannotKeepAspectKeepsOnePixel<TPixel>(TestImageProvider<TPixel> provider) public void ResizeHeightCannotKeepAspectKeepsOnePixel<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -437,7 +435,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ResizeWidthAndKeepAspect<TPixel>(TestImageProvider<TPixel> provider) public void ResizeWidthAndKeepAspect<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -446,12 +444,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
image.Mutate(x => x.Resize(image.Width / 3, 0, false)); image.Mutate(x => x.Resize(image.Width / 3, 0, false));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider, appendPixelTypeToFileName: false);
} }
} }
[Theory] [Theory]
[WithTestPatternImages(100, 10, DefaultPixelType)] [WithTestPatternImages(100, 10, PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ResizeWidthCannotKeepAspectKeepsOnePixel<TPixel>(TestImageProvider<TPixel> provider) public void ResizeWidthCannotKeepAspectKeepsOnePixel<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -464,7 +462,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ResizeWithBoxPadMode<TPixel>(TestImageProvider<TPixel> provider) public void ResizeWithBoxPadMode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -479,12 +477,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
image.Mutate(x => x.Resize(options)); image.Mutate(x => x.Resize(options));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider, appendPixelTypeToFileName: false);
} }
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ResizeWithCropHeightMode<TPixel>(TestImageProvider<TPixel> provider) public void ResizeWithCropHeightMode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -495,12 +493,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
image.Mutate(x => x.Resize(options)); image.Mutate(x => x.Resize(options));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider, appendPixelTypeToFileName: false);
} }
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ResizeWithCropWidthMode<TPixel>(TestImageProvider<TPixel> provider) public void ResizeWithCropWidthMode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -511,12 +509,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
image.Mutate(x => x.Resize(options)); image.Mutate(x => x.Resize(options));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider, appendPixelTypeToFileName: false);
} }
} }
[Theory] [Theory]
[WithFile(TestImages.Jpeg.Issues.IncorrectResize1006, DefaultPixelType)] [WithFile(TestImages.Jpeg.Issues.IncorrectResize1006, PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void CanResizeLargeImageWithCropMode<TPixel>(TestImageProvider<TPixel> provider) public void CanResizeLargeImageWithCropMode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -531,12 +529,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
image.Mutate(x => x.Resize(options)); image.Mutate(x => x.Resize(options));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider, appendPixelTypeToFileName: false);
} }
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ResizeWithMaxMode<TPixel>(TestImageProvider<TPixel> provider) public void ResizeWithMaxMode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -547,12 +545,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
image.Mutate(x => x.Resize(options)); image.Mutate(x => x.Resize(options));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider, appendPixelTypeToFileName: false);
} }
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ResizeWithMinMode<TPixel>(TestImageProvider<TPixel> provider) public void ResizeWithMinMode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -560,21 +558,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{ {
var options = new ResizeOptions var options = new ResizeOptions
{ {
Size = new Size( Size = new Size((int)Math.Round(image.Width * .75F), (int)Math.Round(image.Height * .95F)),
(int)Math.Round(image.Width * .75F),
(int)Math.Round(image.Height * .95F)),
Mode = ResizeMode.Min Mode = ResizeMode.Min
}; };
image.Mutate(x => x.Resize(options)); image.Mutate(x => x.Resize(options));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider, appendPixelTypeToFileName: false);
} }
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ResizeWithPadMode<TPixel>(TestImageProvider<TPixel> provider) public void ResizeWithPadMode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -589,12 +585,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
image.Mutate(x => x.Resize(options)); image.Mutate(x => x.Resize(options));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider, appendPixelTypeToFileName: false);
} }
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ResizeWithStretchMode<TPixel>(TestImageProvider<TPixel> provider) public void ResizeWithStretchMode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -609,14 +605,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
image.Mutate(x => x.Resize(options)); image.Mutate(x => x.Resize(options));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider); image.CompareToReferenceOutput(ValidatorComparer, provider, appendPixelTypeToFileName: false);
} }
} }
[Theory] [Theory]
[WithFile(TestImages.Jpeg.Issues.ExifDecodeOutOfRange694, DefaultPixelType)] [WithFile(TestImages.Jpeg.Issues.ExifDecodeOutOfRange694, PixelTypes.Rgb24)]
[WithFile(TestImages.Jpeg.Issues.ExifGetString750Transform, DefaultPixelType)] [WithFile(TestImages.Jpeg.Issues.ExifGetString750Transform, PixelTypes.Rgb24)]
[WithFile(TestImages.Jpeg.Issues.ExifResize1049, DefaultPixelType)] [WithFile(TestImages.Jpeg.Issues.ExifResize1049, PixelTypes.Rgb24)]
public void CanResizeExifIssueImages<TPixel>(TestImageProvider<TPixel> provider) public void CanResizeExifIssueImages<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {

0
tests/Images/External/ReferenceOutput/ResizeTests/CanResizeLargeImageWithCropMode_Rgba32_issue1006-incorrect-resize.png → tests/Images/External/ReferenceOutput/ResizeTests/CanResizeLargeImageWithCropMode_issue1006-incorrect-resize.png

0
tests/Images/External/ReferenceOutput/ResizeTests/ResizeFromSourceRectangle_Rgba32_CalliphoraPartial.png → tests/Images/External/ReferenceOutput/ResizeTests/ResizeFromSourceRectangle_CalliphoraPartial.png

0
tests/Images/External/ReferenceOutput/ResizeTests/ResizeHeightAndKeepAspect_Rgba32_CalliphoraPartial.png → tests/Images/External/ReferenceOutput/ResizeTests/ResizeHeightAndKeepAspect_CalliphoraPartial.png

0
tests/Images/External/ReferenceOutput/ResizeTests/ResizeWidthAndKeepAspect_Rgba32_CalliphoraPartial.png → tests/Images/External/ReferenceOutput/ResizeTests/ResizeWidthAndKeepAspect_CalliphoraPartial.png

0
tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithBoxPadMode_Rgba32_CalliphoraPartial.png → tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithBoxPadMode_CalliphoraPartial.png

0
tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithCropHeightMode_Rgba32_CalliphoraPartial.png → tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithCropHeightMode_CalliphoraPartial.png

0
tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithCropWidthMode_Rgba32_CalliphoraPartial.png → tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithCropWidthMode_CalliphoraPartial.png

0
tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithMaxMode_Rgba32_CalliphoraPartial.png → tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithMaxMode_CalliphoraPartial.png

0
tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithMinMode_Rgba32_CalliphoraPartial.png → tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithMinMode_CalliphoraPartial.png

0
tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithPadMode_Rgba32_CalliphoraPartial.png → tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithPadMode_CalliphoraPartial.png

0
tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithStretchMode_Rgba32_CalliphoraPartial.png → tests/Images/External/ReferenceOutput/ResizeTests/ResizeWithStretchMode_CalliphoraPartial.png

3
tests/Images/External/ReferenceOutput/ResizeTests/Resize_IsNotBoundToSinglePixelType_Bgr24_TestPattern50x50.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4a9940410cca3fe98a6d7aaf0e2184779f908c569a5a34f9965fb3a4f9e6fa8f
size 1066
Loading…
Cancel
Save