Browse Source

Merge branch 'master' into js/png-decode-to-type

pull/1861/head
James Jackson-South 4 years ago
committed by GitHub
parent
commit
11577100c4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      src/ImageSharp/Common/Helpers/SimdUtils.cs
  2. 82
      src/ImageSharp/Common/Tuples/Vector4Pair.cs
  3. 18
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.Avx2JpegColorConverter.cs
  4. 18
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.BasicJpegColorConverter.cs
  5. 52
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx.cs
  6. 60
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx2.cs
  7. 20
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykScalar.cs
  8. 51
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector.cs
  9. 53
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector8.cs
  10. 24
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs
  11. 53
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleBasic.cs
  12. 34
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleScalar.cs
  13. 38
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleVector.cs
  14. 30
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs
  15. 32
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbBasic.cs
  16. 26
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbScalar.cs
  17. 15
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector.cs
  18. 35
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs
  19. 42
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
  20. 50
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrScalar.cs
  21. 24
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector.cs
  22. 88
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector4.cs
  23. 32
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx.cs
  24. 13
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKScalar.cs
  25. 30
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector.cs
  26. 18
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.Vector8JpegColorConverter.cs
  27. 47
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.VectorizedJpegColorConverter.cs
  28. 32
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterAvx.cs
  29. 114
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterBase.cs
  30. 22
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterScalar.cs
  31. 59
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterVector.cs
  32. 15
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs
  33. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs
  34. 12
      src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
  35. 2
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs
  36. 134
      src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs
  37. 44
      src/ImageSharp/Formats/Webp/Lossy/Vp8Histogram.cs
  38. 16
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs
  39. 14
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs
  40. 18
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs
  41. 24
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs
  42. 16
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs
  43. 1
      tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
  44. 7
      tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
  45. 8
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  46. 3
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  47. 3
      tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
  48. 5
      tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs
  49. 540
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
  50. 3
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  51. 13
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
  52. 1
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  53. 19
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  54. 3
      tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs
  55. 5
      tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs
  56. 5
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  57. 1
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
  58. 3
      tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
  59. 142
      tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs
  60. 111
      tests/ImageSharp.Tests/Formats/WebP/Vp8HistogramTests.cs
  61. 1
      tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
  62. 1
      tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
  63. 14
      tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs

12
src/ImageSharp/Common/Helpers/SimdUtils.cs

@ -33,18 +33,6 @@ namespace SixLabors.ImageSharp
public static bool HasVector4 { get; } =
Vector.IsHardwareAccelerated && Vector<float>.Count == 4;
public static bool HasAvx2
{
get
{
#if SUPPORTS_RUNTIME_INTRINSICS
return Avx2.IsSupported;
#else
return false;
#endif
}
}
/// <summary>
/// Transform all scalars in 'v' in a way that converting them to <see cref="int"/> would have rounding semantics.
/// </summary>

82
src/ImageSharp/Common/Tuples/Vector4Pair.cs

@ -1,82 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Tuples
{
/// <summary>
/// Its faster to process multiple Vector4-s together, so let's pair them!
/// On AVX2 this pair should be convertible to <see cref="Vector{T}"/> of <see cref="float"/>!
/// TODO: Investigate defining this as union with an Octet.OfSingle type.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct Vector4Pair
{
public Vector4 A;
public Vector4 B;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void MultiplyInplace(float value)
{
this.A *= value;
this.B *= value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddInplace(Vector4 value)
{
this.A += value;
this.B += value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddInplace(ref Vector4Pair other)
{
this.A += other.A;
this.B += other.B;
}
/// <summary>.
/// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4! /// TODO: Move it somewhere else.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RoundAndDownscalePreVector8(float downscaleFactor)
{
ref Vector<float> a = ref Unsafe.As<Vector4, Vector<float>>(ref this.A);
a = a.FastRound();
ref Vector<float> b = ref Unsafe.As<Vector4, Vector<float>>(ref this.B);
b = b.FastRound();
// Downscale by 1/factor
var scale = new Vector4(1 / downscaleFactor);
this.A *= scale;
this.B *= scale;
}
/// <summary>
/// AVX2-only Downscale method, specific to Jpeg color conversion.
/// TODO: Move it somewhere else.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RoundAndDownscaleVector8(float downscaleFactor)
{
ref Vector<float> self = ref Unsafe.As<Vector4Pair, Vector<float>>(ref this);
Vector<float> v = self;
v = v.FastRound();
// Downscale by 1/factor
v *= new Vector<float>(1 / downscaleFactor);
self = v;
}
public override string ToString()
{
return $"{nameof(Vector4Pair)}({this.A}, {this.B})";
}
}
}

18
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.Avx2JpegColorConverter.cs

@ -1,18 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal abstract class Avx2JpegColorConverter : VectorizedJpegColorConverter
{
protected Avx2JpegColorConverter(JpegColorSpace colorSpace, int precision)
: base(colorSpace, precision, 8)
{
}
protected sealed override bool IsAvailable => SimdUtils.HasAvx2;
}
}
}

18
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.BasicJpegColorConverter.cs

@ -1,18 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal abstract class BasicJpegColorConverter : JpegColorConverter
{
protected BasicJpegColorConverter(JpegColorSpace colorSpace, int precision)
: base(colorSpace, precision)
{
}
protected override bool IsAvailable => true;
}
}
}

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

@ -0,0 +1,52 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromCmykAvx : JpegColorConverterAvx
{
public FromCmykAvx(int precision)
: base(JpegColorSpace.Cmyk, precision)
{
}
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> c1Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> c2Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector256<float> c3Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3));
// Used for the color conversion
var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue));
nint n = values.Component0.Length / Vector256<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector256<float> c = ref Unsafe.Add(ref c0Base, i);
ref Vector256<float> m = ref Unsafe.Add(ref c1Base, i);
ref Vector256<float> y = ref Unsafe.Add(ref c2Base, i);
Vector256<float> k = Unsafe.Add(ref c3Base, i);
k = Avx.Multiply(k, scale);
c = Avx.Multiply(c, k);
m = Avx.Multiply(m, k);
y = Avx.Multiply(y, k);
}
}
}
}
}
#endif

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

@ -1,60 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using static SixLabors.ImageSharp.SimdUtils;
#endif
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal sealed class FromCmykAvx2 : Avx2JpegColorConverter
{
public FromCmykAvx2(int precision)
: base(JpegColorSpace.Cmyk, precision)
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{
#if SUPPORTS_RUNTIME_INTRINSICS
ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> c1Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector256<float> c2Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector256<float> c3Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3));
// Used for the color conversion
var scale = Vector256.Create(1 / this.MaximumValue);
nint n = values.Component0.Length / 8;
for (nint i = 0; i < n; i++)
{
ref Vector256<float> c = ref Unsafe.Add(ref c0Base, i);
ref Vector256<float> m = ref Unsafe.Add(ref c1Base, i);
ref Vector256<float> y = ref Unsafe.Add(ref c2Base, i);
Vector256<float> k = Unsafe.Add(ref c3Base, i);
k = Avx.Multiply(k, scale);
c = Avx.Multiply(Avx.Multiply(c, k), scale);
m = Avx.Multiply(Avx.Multiply(m, k), scale);
y = Avx.Multiply(Avx.Multiply(y, k), scale);
}
#endif
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromCmykBasic.ConvertCoreInplace(values, this.MaximumValue);
}
}
}

20
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykBasic.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykScalar.cs

@ -1,16 +1,15 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromCmykBasic : BasicJpegColorConverter
internal sealed class FromCmykScalar : JpegColorConverterScalar
{
public FromCmykBasic(int precision)
public FromCmykScalar(int precision)
: base(JpegColorSpace.Cmyk, precision)
{
}
@ -25,17 +24,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
Span<float> c2 = values.Component2;
Span<float> c3 = values.Component3;
float scale = 1 / maxValue;
float scale = 1 / (maxValue * maxValue);
for (int i = 0; i < c0.Length; i++)
{
float c = c0[i];
float m = c1[i];
float y = c2[i];
float k = c3[i] / maxValue;
float k = c3[i];
c0[i] = c * k * scale;
c1[i] = m * k * scale;
c2[i] = y * k * scale;
k *= scale;
c0[i] = c * k;
c1[i] = m * k;
c2[i] = y * k;
}
}
}

51
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykVector.cs

@ -0,0 +1,51 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromCmykVector : JpegColorConverterVector
{
public FromCmykVector(int precision)
: base(JpegColorSpace.Cmyk, precision)
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{
ref Vector<float> cBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> mBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> yBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector<float> kBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3));
var scale = new Vector<float>(1 / (this.MaximumValue * this.MaximumValue));
nint n = values.Component0.Length / Vector<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector<float> c = ref Unsafe.Add(ref cBase, i);
ref Vector<float> m = ref Unsafe.Add(ref mBase, i);
ref Vector<float> y = ref Unsafe.Add(ref yBase, i);
Vector<float> k = Unsafe.Add(ref kBase, i);
k *= scale;
c *= k;
m *= k;
y *= k;
}
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromCmykScalar.ConvertCoreInplace(values, this.MaximumValue);
}
}
}

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

@ -1,53 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Tuples;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal sealed class FromCmykVector8 : Vector8JpegColorConverter
{
public FromCmykVector8(int precision)
: base(JpegColorSpace.Cmyk, precision)
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{
ref Vector<float> cBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> mBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> yBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector<float> kBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3));
var scale = new Vector<float>(1 / this.MaximumValue);
// Walking 8 elements at one step:
nint n = values.Component0.Length / 8;
for (nint i = 0; i < n; i++)
{
ref Vector<float> c = ref Unsafe.Add(ref cBase, i);
ref Vector<float> m = ref Unsafe.Add(ref mBase, i);
ref Vector<float> y = ref Unsafe.Add(ref yBase, i);
Vector<float> k = Unsafe.Add(ref kBase, i) * scale;
c = (c * k) * scale;
m = (m * k) * scale;
y = (y * k) * scale;
}
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromCmykBasic.ConvertCoreInplace(values, this.MaximumValue);
}
}
}

24
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx2.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs

@ -1,47 +1,39 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using static SixLabors.ImageSharp.SimdUtils;
#endif
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromGrayscaleAvx2 : Avx2JpegColorConverter
internal sealed class FromGrayscaleAvx : JpegColorConverterAvx
{
public FromGrayscaleAvx2(int precision)
public FromGrayscaleAvx(int precision)
: base(JpegColorSpace.Grayscale, precision)
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
public override void ConvertToRgbInplace(in ComponentValues values)
{
#if SUPPORTS_RUNTIME_INTRINSICS
ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
// Used for the color conversion
var scale = Vector256.Create(1 / this.MaximumValue);
nint n = values.Component0.Length / 8;
nint n = values.Component0.Length / Vector256<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i);
c0 = Avx.Multiply(c0, scale);
}
#endif
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromGrayscaleBasic.ScaleValues(values.Component0, this.MaximumValue);
}
}
}
#endif

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

@ -1,53 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal sealed class FromGrayscaleBasic : BasicJpegColorConverter
{
public FromGrayscaleBasic(int precision)
: base(JpegColorSpace.Grayscale, precision)
{
}
public override void ConvertToRgbInplace(in ComponentValues values) =>
ScaleValues(values.Component0, this.MaximumValue);
internal static void ScaleValues(Span<float> values, float maxValue)
{
Span<Vector4> vecValues = MemoryMarshal.Cast<float, Vector4>(values);
var scaleVector = new Vector4(1 / maxValue);
for (int i = 0; i < vecValues.Length; i++)
{
vecValues[i] *= scaleVector;
}
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;
}
}
}
}
}
}
}

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

@ -0,0 +1,34 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromGrayscaleScalar : JpegColorConverterScalar
{
public FromGrayscaleScalar(int precision)
: base(JpegColorSpace.Grayscale, precision)
{
}
public override void ConvertToRgbInplace(in ComponentValues values) =>
ConvertCoreInplace(values.Component0, this.MaximumValue);
internal static void ConvertCoreInplace(Span<float> values, float maxValue)
{
ref float valuesRef = ref MemoryMarshal.GetReference(values);
float scale = 1 / maxValue;
for (nint i = 0; i < values.Length; i++)
{
Unsafe.Add(ref valuesRef, i) *= scale;
}
}
}
}
}

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

@ -0,0 +1,38 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromGrayScaleVector : JpegColorConverterVector
{
public FromGrayScaleVector(int precision)
: base(JpegColorSpace.Grayscale, precision)
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{
ref Vector<float> cBase =
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
var scale = new Vector<float>(1 / this.MaximumValue);
nint n = values.Component0.Length / Vector<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector<float> c0 = ref Unsafe.Add(ref cBase, i);
c0 *= scale;
}
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromGrayscaleScalar.ConvertCoreInplace(values.Component0, this.MaximumValue);
}
}
}

30
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx2.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs

@ -1,40 +1,35 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using static SixLabors.ImageSharp.SimdUtils;
#endif
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromRgbAvx2 : Avx2JpegColorConverter
internal sealed class FromRgbAvx : JpegColorConverterAvx
{
public FromRgbAvx2(int precision)
public FromRgbAvx(int precision)
: base(JpegColorSpace.RGB, precision)
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
public override void ConvertToRgbInplace(in ComponentValues values)
{
#if SUPPORTS_RUNTIME_INTRINSICS
ref Vector256<float> rBase =
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> gBase =
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> bBase =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2));
// Used for the color conversion
var scale = Vector256.Create(1 / this.MaximumValue);
nint n = values.Component0.Length / 8;
nint n = values.Component0.Length / Vector256<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector256<float> r = ref Unsafe.Add(ref rBase, i);
@ -44,11 +39,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
g = Avx.Multiply(g, scale);
b = Avx.Multiply(b, scale);
}
#endif
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromRgbBasic.ConvertCoreInplace(values, this.MaximumValue);
}
}
}
#endif

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

@ -1,32 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal sealed class FromRgbBasic : BasicJpegColorConverter
{
public FromRgbBasic(int precision)
: base(JpegColorSpace.RGB, precision)
{
}
public override void ConvertToRgbInplace(in ComponentValues values)
{
ConvertCoreInplace(values, this.MaximumValue);
}
internal static void ConvertCoreInplace(ComponentValues values, float maxValue)
{
FromGrayscaleBasic.ScaleValues(values.Component0, maxValue);
FromGrayscaleBasic.ScaleValues(values.Component1, maxValue);
FromGrayscaleBasic.ScaleValues(values.Component2, maxValue);
}
}
}
}

26
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbScalar.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromRgbScalar : JpegColorConverterScalar
{
public FromRgbScalar(int precision)
: base(JpegColorSpace.RGB, precision)
{
}
public override void ConvertToRgbInplace(in ComponentValues values) =>
ConvertCoreInplace(values, this.MaximumValue);
internal static void ConvertCoreInplace(ComponentValues values, float maxValue)
{
FromGrayscaleScalar.ConvertCoreInplace(values.Component0, maxValue);
FromGrayscaleScalar.ConvertCoreInplace(values.Component1, maxValue);
FromGrayscaleScalar.ConvertCoreInplace(values.Component2, maxValue);
}
}
}
}

15
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector8.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector.cs

@ -1,19 +1,17 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Tuples;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromRgbVector8 : Vector8JpegColorConverter
internal sealed class FromRgbVector : JpegColorConverterVector
{
public FromRgbVector8(int precision)
public FromRgbVector(int precision)
: base(JpegColorSpace.RGB, precision)
{
}
@ -29,8 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var scale = new Vector<float>(1 / this.MaximumValue);
// Walking 8 elements at one step:
nint n = values.Component0.Length / 8;
nint n = values.Component0.Length / Vector<float>.Count;
for (nint i = 0; i < n; i++)
{
ref Vector<float> r = ref Unsafe.Add(ref rBase, i);
@ -43,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromRgbBasic.ConvertCoreInplace(values, this.MaximumValue);
FromRgbScalar.ConvertCoreInplace(values, this.MaximumValue);
}
}
}

35
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx2.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs

@ -1,31 +1,27 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using static SixLabors.ImageSharp.SimdUtils;
#endif
// ReSharper disable ImpureMethodCallOnReadonlyValueField
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromYCbCrAvx2 : Avx2JpegColorConverter
internal sealed class FromYCbCrAvx : JpegColorConverterAvx
{
public FromYCbCrAvx2(int precision)
public FromYCbCrAvx(int precision)
: base(JpegColorSpace.YCbCr, precision)
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
public override void ConvertToRgbInplace(in ComponentValues values)
{
#if SUPPORTS_RUNTIME_INTRINSICS
ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> c1Base =
@ -36,18 +32,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// Used for the color conversion
var chromaOffset = Vector256.Create(-this.HalfValue);
var scale = Vector256.Create(1 / this.MaximumValue);
var rCrMult = Vector256.Create(1.402F);
var gCbMult = Vector256.Create(-0.344136F);
var gCrMult = Vector256.Create(-0.714136F);
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);
var rCrMult = Vector256.Create(FromYCbCrScalar.RCrMult);
var gCbMult = Vector256.Create(-FromYCbCrScalar.GCbMult);
var gCrMult = Vector256.Create(-FromYCbCrScalar.GCrMult);
var bCbMult = Vector256.Create(FromYCbCrScalar.BCbMult);
// Walking 8 elements at one step:
nint n = values.Component0.Length / 8;
nint n = values.Component0.Length / Vector256<float>.Count;
for (nint i = 0; i < n; i++)
{
// y = yVals[i];
@ -64,7 +55,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
// Adding & multiplying 8 elements at one time:
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult);
Vector256<float> g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult);
@ -77,11 +67,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
c1 = g;
c2 = b;
}
#endif
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromYCbCrBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
}
}
}
#endif

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

@ -1,42 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal sealed class FromYCbCrBasic : BasicJpegColorConverter
{
public FromYCbCrBasic(int precision)
: base(JpegColorSpace.YCbCr, precision)
{
}
public override void ConvertToRgbInplace(in ComponentValues values)
=> ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
internal static void ConvertCoreInplace(in ComponentValues values, float maxValue, float halfValue)
{
Span<float> c0 = values.Component0;
Span<float> c1 = values.Component1;
Span<float> c2 = values.Component2;
var scale = 1 / maxValue;
for (int i = 0; i < c0.Length; i++)
{
float y = c0[i];
float cb = c1[i] - halfValue;
float cr = c2[i] - halfValue;
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;
}
}
}
}
}

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

@ -0,0 +1,50 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromYCbCrScalar : JpegColorConverterScalar
{
// TODO: comments, derived from ITU-T Rec. T.871
internal const float RCrMult = 1.402f;
internal const float GCbMult = (float)(0.114 * 1.772 / 0.587);
internal const float GCrMult = (float)(0.299 * 1.402 / 0.587);
internal const float BCbMult = 1.772f;
public FromYCbCrScalar(int precision)
: base(JpegColorSpace.YCbCr, precision)
{
}
public override void ConvertToRgbInplace(in ComponentValues values)
=> ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
internal static void ConvertCoreInplace(in ComponentValues values, float maxValue, float halfValue)
{
Span<float> c0 = values.Component0;
Span<float> c1 = values.Component1;
Span<float> c2 = values.Component2;
float scale = 1 / maxValue;
for (int i = 0; i < c0.Length; i++)
{
float y = c0[i];
float cb = c1[i] - halfValue;
float cr = c2[i] - halfValue;
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
c0[i] = MathF.Round(y + (RCrMult * cr), MidpointRounding.AwayFromZero) * scale;
c1[i] = MathF.Round(y - (GCbMult * cb) - (GCrMult * cr), MidpointRounding.AwayFromZero) * scale;
c2[i] = MathF.Round(y + (BCbMult * cb), MidpointRounding.AwayFromZero) * scale;
}
}
}
}
}

24
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector8.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrVector.cs

@ -1,20 +1,18 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Tuples;
// ReSharper disable ImpureMethodCallOnReadonlyValueField
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromYCbCrVector8 : Vector8JpegColorConverter
internal sealed class FromYCbCrVector : JpegColorConverterVector
{
public FromYCbCrVector8(int precision)
public FromYCbCrVector(int precision)
: base(JpegColorSpace.YCbCr, precision)
{
}
@ -30,10 +28,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var chromaOffset = new Vector<float>(-this.HalfValue);
// Walking 8 elements at one step:
nint n = values.Component0.Length / 8;
var scale = new Vector<float>(1 / this.MaximumValue);
var rCrMult = new Vector<float>(FromYCbCrScalar.RCrMult);
var gCbMult = new Vector<float>(-FromYCbCrScalar.GCbMult);
var gCrMult = new Vector<float>(-FromYCbCrScalar.GCrMult);
var bCbMult = new Vector<float>(FromYCbCrScalar.BCbMult);
nint n = values.Component0.Length / Vector<float>.Count;
for (nint i = 0; i < n; i++)
{
// y = yVals[i];
@ -49,10 +50,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
// Adding & multiplying 8 elements at one time:
Vector<float> r = y + (cr * new Vector<float>(1.402F));
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> r = y + (cr * rCrMult);
Vector<float> g = y + (cb * gCbMult) + (cr * gCrMult);
Vector<float> b = y + (cb * bCbMult);
r = r.FastRound();
g = g.FastRound();
@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromYCbCrBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
FromYCbCrScalar.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
}
}
}

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

@ -1,88 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Tuples;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal sealed class FromYCbCrVector4 : VectorizedJpegColorConverter
{
public FromYCbCrVector4(int precision)
: base(JpegColorSpace.YCbCr, precision, 8)
{
}
protected override bool IsAvailable => SimdUtils.HasVector4;
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
{
DebugGuard.IsTrue(values.Component0.Length % 8 == 0, nameof(values), "Length should be divisible by 8!");
ref Vector4Pair c0Base =
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector4Pair c1Base =
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector4Pair c2Base =
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component2));
var chromaOffset = new Vector4(-this.HalfValue);
var maxValue = this.MaximumValue;
// Walking 8 elements at one step:
nint n = values.Component0.Length / 8;
for (nint i = 0; i < n; i++)
{
// y = yVals[i];
ref Vector4Pair c0 = ref Unsafe.Add(ref c0Base, i);
// cb = cbVals[i] - halfValue);
ref Vector4Pair c1 = ref Unsafe.Add(ref c1Base, i);
c1.AddInplace(chromaOffset);
// cr = crVals[i] - halfValue;
ref Vector4Pair c2 = ref Unsafe.Add(ref c2Base, i);
c2.AddInplace(chromaOffset);
// r = y + (1.402F * cr);
Vector4Pair r = c0;
Vector4Pair tmp = c2;
tmp.MultiplyInplace(1.402F);
r.AddInplace(ref tmp);
// g = y - (0.344136F * cb) - (0.714136F * cr);
Vector4Pair g = c0;
tmp = c1;
tmp.MultiplyInplace(-0.344136F);
g.AddInplace(ref tmp);
tmp = c2;
tmp.MultiplyInplace(-0.714136F);
g.AddInplace(ref tmp);
// b = y + (1.772F * cb);
Vector4Pair b = c0;
tmp = c1;
tmp.MultiplyInplace(1.772F);
b.AddInplace(ref tmp);
r.RoundAndDownscalePreVector8(maxValue);
g.RoundAndDownscalePreVector8(maxValue);
b.RoundAndDownscalePreVector8(maxValue);
c0 = r;
c1 = g;
c2 = b;
}
}
protected override void ConvertCoreInplace(in ComponentValues values)
=> FromYCbCrBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
}
}
}

32
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx2.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx.cs

@ -1,30 +1,26 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using static SixLabors.ImageSharp.SimdUtils;
#endif
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromYccKAvx2 : Avx2JpegColorConverter
internal sealed class FromYccKAvx : JpegColorConverterAvx
{
public FromYccKAvx2(int precision)
public FromYccKAvx(int precision)
: base(JpegColorSpace.Ycck, precision)
{
}
protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
public override void ConvertToRgbInplace(in ComponentValues values)
{
#if SUPPORTS_RUNTIME_INTRINSICS
ref Vector256<float> c0Base =
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector256<float> c1Base =
@ -38,13 +34,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var chromaOffset = Vector256.Create(-this.HalfValue);
var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue));
var max = Vector256.Create(this.MaximumValue);
var rCrMult = Vector256.Create(1.402F);
var gCbMult = Vector256.Create(-0.344136F);
var gCrMult = Vector256.Create(-0.714136F);
var bCbMult = Vector256.Create(1.772F);
var rCrMult = Vector256.Create(FromYCbCrScalar.RCrMult);
var gCbMult = Vector256.Create(-FromYCbCrScalar.GCbMult);
var gCrMult = Vector256.Create(-FromYCbCrScalar.GCrMult);
var bCbMult = Vector256.Create(FromYCbCrScalar.BCbMult);
// Walking 8 elements at one step:
nint n = values.Component0.Length / 8;
nint n = values.Component0.Length / Vector256<float>.Count;
for (nint i = 0; i < n; i++)
{
// y = yVals[i];
@ -62,7 +58,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
// Adding & multiplying 8 elements at one time:
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult);
Vector256<float> g =
HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
@ -80,11 +75,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
c1 = g;
c2 = b;
}
#endif
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromYccKBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
}
}
}
#endif

13
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKBasic.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKScalar.cs

@ -1,16 +1,15 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromYccKBasic : BasicJpegColorConverter
internal sealed class FromYccKScalar : JpegColorConverterScalar
{
public FromYccKBasic(int precision)
public FromYccKScalar(int precision)
: base(JpegColorSpace.Ycck, precision)
{
}
@ -25,9 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
Span<float> c2 = values.Component2;
Span<float> c3 = values.Component3;
var v = new Vector4(0, 0, 0, 1F);
var scale = 1 / (maxValue * maxValue);
float scale = 1 / (maxValue * maxValue);
for (int i = 0; i < values.Component0.Length; i++)
{

30
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector8.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKVector.cs

@ -1,19 +1,17 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Tuples;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
internal abstract partial class JpegColorConverterBase
{
internal sealed class FromYccKVector8 : Vector8JpegColorConverter
internal sealed class FromYccKVector : JpegColorConverterVector
{
public FromYccKVector8(int precision)
public FromYccKVector(int precision)
: base(JpegColorSpace.Ycck, precision)
{
}
@ -30,13 +28,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3));
var chromaOffset = new Vector<float>(-this.HalfValue);
// Walking 8 elements at one step:
nint n = values.Component0.Length / 8;
var scale = new Vector<float>(1 / (this.MaximumValue * this.MaximumValue));
var max = new Vector<float>(this.MaximumValue);
var scale = new Vector<float>(1f) / (max * max);
var rCrMult = new Vector<float>(FromYCbCrScalar.RCrMult);
var gCbMult = new Vector<float>(-FromYCbCrScalar.GCbMult);
var gCrMult = new Vector<float>(-FromYCbCrScalar.GCrMult);
var bCbMult = new Vector<float>(FromYCbCrScalar.BCbMult);
nint n = values.Component0.Length / Vector<float>.Count;
for (nint i = 0; i < n; i++)
{
// y = yVals[i];
@ -55,10 +54,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
// Adding & multiplying 8 elements at one time:
Vector<float> r = y + (cr * new Vector<float>(1.402F));
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> r = y + (cr * rCrMult);
Vector<float> g = y + (cb * gCbMult) + (cr * gCrMult);
Vector<float> b = y + (cb * bCbMult);
r = (max - r.FastRound()) * scaledK;
g = (max - g.FastRound()) * scaledK;
@ -71,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
protected override void ConvertCoreInplace(in ComponentValues values) =>
FromYccKBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
FromYccKScalar.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue);
}
}
}

18
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.Vector8JpegColorConverter.cs

@ -1,18 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal abstract class Vector8JpegColorConverter : VectorizedJpegColorConverter
{
protected Vector8JpegColorConverter(JpegColorSpace colorSpace, int precision)
: base(colorSpace, precision, 8)
{
}
protected sealed override bool IsAvailable => SimdUtils.HasVector8;
}
}
}

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

@ -1,47 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal abstract class VectorizedJpegColorConverter : JpegColorConverter
{
private readonly int vectorSize;
protected VectorizedJpegColorConverter(JpegColorSpace colorSpace, int precision, int vectorSize)
: base(colorSpace, precision)
{
this.vectorSize = vectorSize;
}
public override void ConvertToRgbInplace(in ComponentValues values)
{
int length = values.Component0.Length;
int remainder = values.Component0.Length % this.vectorSize;
int simdCount = length - remainder;
if (simdCount > 0)
{
// This implementation is actually AVX specific.
// An AVX register is capable of storing 8 float-s.
if (!this.IsAvailable)
{
throw new InvalidOperationException(
"This converter can be used only on architecture having 256 byte floating point SIMD registers!");
}
this.ConvertCoreVectorizedInplace(values.Slice(0, simdCount));
}
this.ConvertCoreInplace(values.Slice(simdCount, remainder));
}
protected virtual void ConvertCoreVectorizedInplace(in ComponentValues values) => throw new NotImplementedException();
protected virtual void ConvertCoreInplace(in ComponentValues values) => throw new NotImplementedException();
}
}
}

32
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterAvx.cs

@ -0,0 +1,32 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverterBase
{
/// <summary>
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
/// based on <see cref="Avx"/> instructions.
/// </summary>
/// <remarks>
/// Converters of this family would expect input buffers lengths to be
/// divisible by 8 without a remainder.
/// This is guaranteed by real-life data as jpeg stores pixels via 8x8 blocks.
/// DO NOT pass test data of invalid size to these converters as they
/// potentially won't do a bound check and return a false positive result.
/// </remarks>
internal abstract class JpegColorConverterAvx : JpegColorConverterBase
{
protected JpegColorConverterAvx(JpegColorSpace colorSpace, int precision)
: base(colorSpace, precision)
{
}
public override bool IsAvailable => Avx.IsSupported;
}
}
}
#endif

114
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs → src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterBase.cs

@ -4,26 +4,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Tuples;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
/// <summary>
/// Encapsulates the conversion of Jpeg channels to RGBA values packed in <see cref="Vector4"/> buffer.
/// Encapsulates the conversion of color channels from jpeg image to RGB channels.
/// </summary>
internal abstract partial class JpegColorConverter
internal abstract partial class JpegColorConverterBase
{
/// <summary>
/// The available converters
/// </summary>
private static readonly JpegColorConverter[] Converters = CreateConverters();
private static readonly JpegColorConverterBase[] Converters = CreateConverters();
/// <summary>
/// Initializes a new instance of the <see cref="JpegColorConverter"/> class.
/// Initializes a new instance of the <see cref="JpegColorConverterBase"/> class.
/// </summary>
protected JpegColorConverter(JpegColorSpace colorSpace, int precision)
protected JpegColorConverterBase(JpegColorSpace colorSpace, int precision)
{
this.ColorSpace = colorSpace;
this.Precision = precision;
@ -32,10 +30,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
/// <summary>
/// Gets a value indicating whether this <see cref="JpegColorConverter"/> is available
/// Gets a value indicating whether this <see cref="JpegColorConverterBase"/> is available
/// on the current runtime and CPU architecture.
/// </summary>
protected abstract bool IsAvailable { get; }
public abstract bool IsAvailable { get; }
/// <summary>
/// Gets the <see cref="JpegColorSpace"/> of this converter.
@ -58,11 +56,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
private float HalfValue { get; }
/// <summary>
/// Returns the <see cref="JpegColorConverter"/> corresponding to the given <see cref="JpegColorSpace"/>
/// Returns the <see cref="JpegColorConverterBase"/> corresponding to the given <see cref="JpegColorSpace"/>
/// </summary>
public static JpegColorConverter GetConverter(JpegColorSpace colorSpace, int precision)
public static JpegColorConverterBase GetConverter(JpegColorSpace colorSpace, int precision)
{
JpegColorConverter converter = Array.Find(
JpegColorConverterBase converter = Array.Find(
Converters,
c => c.ColorSpace == colorSpace
&& c.Precision == precision);
@ -82,11 +80,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
public abstract void ConvertToRgbInplace(in ComponentValues values);
/// <summary>
/// Returns the <see cref="JpegColorConverter"/>s for all supported colorspaces and precisions.
/// Returns the <see cref="JpegColorConverterBase"/>s for all supported colorspaces and precisions.
/// </summary>
private static JpegColorConverter[] CreateConverters()
private static JpegColorConverterBase[] CreateConverters()
{
var converters = new List<JpegColorConverter>();
var converters = new List<JpegColorConverterBase>();
// 8-bit converters
converters.AddRange(GetYCbCrConverters(8));
@ -106,63 +104,63 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
/// <summary>
/// Returns the <see cref="JpegColorConverter"/>s for the YCbCr colorspace.
/// Returns the <see cref="JpegColorConverterBase"/>s for the YCbCr colorspace.
/// </summary>
private static IEnumerable<JpegColorConverter> GetYCbCrConverters(int precision)
private static IEnumerable<JpegColorConverterBase> GetYCbCrConverters(int precision)
{
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromYCbCrAvx2(precision);
yield return new FromYCbCrAvx(precision);
#endif
yield return new FromYCbCrVector8(precision);
yield return new FromYCbCrVector4(precision);
yield return new FromYCbCrBasic(precision);
yield return new FromYCbCrVector(precision);
yield return new FromYCbCrScalar(precision);
}
/// <summary>
/// Returns the <see cref="JpegColorConverter"/>s for the YccK colorspace.
/// Returns the <see cref="JpegColorConverterBase"/>s for the YccK colorspace.
/// </summary>
private static IEnumerable<JpegColorConverter> GetYccKConverters(int precision)
private static IEnumerable<JpegColorConverterBase> GetYccKConverters(int precision)
{
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromYccKAvx2(precision);
yield return new FromYccKAvx(precision);
#endif
yield return new FromYccKVector8(precision);
yield return new FromYccKBasic(precision);
yield return new FromYccKVector(precision);
yield return new FromYccKScalar(precision);
}
/// <summary>
/// Returns the <see cref="JpegColorConverter"/>s for the CMYK colorspace.
/// Returns the <see cref="JpegColorConverterBase"/>s for the CMYK colorspace.
/// </summary>
private static IEnumerable<JpegColorConverter> GetCmykConverters(int precision)
private static IEnumerable<JpegColorConverterBase> GetCmykConverters(int precision)
{
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromCmykAvx2(precision);
yield return new FromCmykAvx(precision);
#endif
yield return new FromCmykVector8(precision);
yield return new FromCmykBasic(precision);
yield return new FromCmykVector(precision);
yield return new FromCmykScalar(precision);
}
/// <summary>
/// Returns the <see cref="JpegColorConverter"/>s for the gray scale colorspace.
/// Returns the <see cref="JpegColorConverterBase"/>s for the gray scale colorspace.
/// </summary>
private static IEnumerable<JpegColorConverter> GetGrayScaleConverters(int precision)
private static IEnumerable<JpegColorConverterBase> GetGrayScaleConverters(int precision)
{
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromGrayscaleAvx2(precision);
yield return new FromGrayscaleAvx(precision);
#endif
yield return new FromGrayscaleBasic(precision);
yield return new FromGrayScaleVector(precision);
yield return new FromGrayscaleScalar(precision);
}
/// <summary>
/// Returns the <see cref="JpegColorConverter"/>s for the RGB colorspace.
/// Returns the <see cref="JpegColorConverterBase"/>s for the RGB colorspace.
/// </summary>
private static IEnumerable<JpegColorConverter> GetRgbConverters(int precision)
private static IEnumerable<JpegColorConverterBase> GetRgbConverters(int precision)
{
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromRgbAvx2(precision);
yield return new FromRgbAvx(precision);
#endif
yield return new FromRgbVector8(precision);
yield return new FromRgbBasic(precision);
yield return new FromRgbVector(precision);
yield return new FromRgbScalar(precision);
}
/// <summary>
@ -200,35 +198,39 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// <summary>
/// Initializes a new instance of the <see cref="ComponentValues"/> struct.
/// </summary>
/// <param name="componentProcessors">The 1-4 sized list of component post processors.</param>
/// <param name="row">The row to convert</param>
public ComponentValues(IReadOnlyList<JpegComponentPostProcessor> componentProcessors, int row)
/// <param name="componentBuffers">List of component buffers.</param>
/// <param name="row">Row to convert</param>
public ComponentValues(IReadOnlyList<Buffer2D<float>> componentBuffers, int row)
{
this.ComponentCount = componentProcessors.Count;
DebugGuard.MustBeGreaterThan(componentBuffers.Count, 0, nameof(componentBuffers));
this.ComponentCount = componentBuffers.Count;
this.Component0 = componentProcessors[0].GetColorBufferRowSpan(row);
this.Component0 = componentBuffers[0].DangerousGetRowSpan(row);
// In case of grayscale, Component1 and Component2 point to Component0 memory area
this.Component1 = this.ComponentCount > 1 ? componentProcessors[1].GetColorBufferRowSpan(row) : this.Component0;
this.Component2 = this.ComponentCount > 2 ? componentProcessors[2].GetColorBufferRowSpan(row) : this.Component0;
this.Component3 = this.ComponentCount > 3 ? componentProcessors[3].GetColorBufferRowSpan(row) : Span<float>.Empty;
this.Component1 = this.ComponentCount > 1 ? componentBuffers[1].DangerousGetRowSpan(row) : this.Component0;
this.Component2 = this.ComponentCount > 2 ? componentBuffers[2].DangerousGetRowSpan(row) : this.Component0;
this.Component3 = this.ComponentCount > 3 ? componentBuffers[3].DangerousGetRowSpan(row) : Span<float>.Empty;
}
/// <summary>
/// Initializes a new instance of the <see cref="ComponentValues"/> struct.
/// </summary>
/// <param name="componentBuffers">The 1-4 sized list of component buffers.</param>
/// <param name="row">The row to convert</param>
public ComponentValues(IReadOnlyList<Buffer2D<float>> componentBuffers, int row)
/// <param name="processors">List of component color processors.</param>
/// <param name="row">Row to convert</param>
public ComponentValues(IReadOnlyList<JpegComponentPostProcessor> processors, int row)
{
this.ComponentCount = componentBuffers.Count;
DebugGuard.MustBeGreaterThan(processors.Count, 0, nameof(processors));
this.Component0 = componentBuffers[0].DangerousGetRowSpan(row);
this.ComponentCount = processors.Count;
this.Component0 = processors[0].GetColorBufferRowSpan(row);
// In case of grayscale, Component1 and Component2 point to Component0 memory area
this.Component1 = this.ComponentCount > 1 ? componentBuffers[1].DangerousGetRowSpan(row) : this.Component0;
this.Component2 = this.ComponentCount > 2 ? componentBuffers[2].DangerousGetRowSpan(row) : this.Component0;
this.Component3 = this.ComponentCount > 3 ? componentBuffers[3].DangerousGetRowSpan(row) : Span<float>.Empty;
this.Component1 = this.ComponentCount > 1 ? processors[1].GetColorBufferRowSpan(row) : this.Component0;
this.Component2 = this.ComponentCount > 2 ? processors[2].GetColorBufferRowSpan(row) : this.Component0;
this.Component3 = this.ComponentCount > 3 ? processors[3].GetColorBufferRowSpan(row) : Span<float>.Empty;
}
internal ComponentValues(

22
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterScalar.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverterBase
{
/// <summary>
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
/// based on scalar instructions.
/// </summary>
internal abstract class JpegColorConverterScalar : JpegColorConverterBase
{
protected JpegColorConverterScalar(JpegColorSpace colorSpace, int precision)
: base(colorSpace, precision)
{
}
public override bool IsAvailable => true;
}
}
}

59
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterVector.cs

@ -0,0 +1,59 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverterBase
{
/// <summary>
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
/// based on <see cref="Vector"/> API.
/// </summary>
/// <remarks>
/// Converters of this family can work with data of any size.
/// Even though real life data is guaranteed to be of size
/// divisible by 8 newer SIMD instructions like AVX512 won't work with
/// such data out of the box. These converters have fallback code
/// for 'remainder' data.
/// </remarks>
internal abstract class JpegColorConverterVector : JpegColorConverterBase
{
protected JpegColorConverterVector(JpegColorSpace colorSpace, int precision)
: base(colorSpace, precision)
{
}
public sealed override bool IsAvailable => Vector.IsHardwareAccelerated && Vector<float>.Count % 4 == 0;
public override void ConvertToRgbInplace(in ComponentValues values)
{
DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware.");
int length = values.Component0.Length;
int remainder = (int)((uint)length % (uint)Vector<float>.Count);
// Jpeg images are guaranteed to have pixel strides at least 8 pixels wide
// Thus there's no need to check whether simdCount is greater than zero
int simdCount = length - remainder;
this.ConvertCoreVectorizedInplace(values.Slice(0, simdCount));
// Jpeg images width is always divisible by 8 without a remainder
// so it's safe to say SSE/AVX implementations would never have
// 'remainder' pixels
// But some exotic simd implementations e.g. AVX-512 can have
// remainder pixels
if (remainder > 0)
{
this.ConvertCoreInplace(values.Slice(simdCount, remainder));
}
}
protected virtual void ConvertCoreVectorizedInplace(in ComponentValues values) => throw new NotImplementedException();
protected virtual void ConvertCoreInplace(in ComponentValues values) => throw new NotImplementedException();
}
}
}

15
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegColorSpace.cs

@ -10,14 +10,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
Undefined = 0,
/// <summary>
/// Color space with 1 component.
/// </summary>
Grayscale,
/// <summary>
/// Color space with 4 components.
/// </summary>
Ycck,
/// <summary>
/// Color space with 4 components.
/// </summary>
Cmyk,
/// <summary>
/// Color space with 3 components.
/// </summary>
RGB,
/// <summary>
/// Color space with 3 components.
/// </summary>
YCbCr
}
}

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs

@ -57,6 +57,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <param name="frame">The jpeg frame with the color space to convert to.</param>
/// <param name="jpegData">The raw JPEG data.</param>
/// <returns>The color converter.</returns>
protected virtual JpegColorConverter GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverter.GetConverter(jpegData.ColorSpace, frame.Precision);
protected virtual JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverterBase.GetConverter(jpegData.ColorSpace, frame.Precision);
}
}

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

@ -14,11 +14,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <inheritdoc/>
/// <remarks>
/// Color decoding scheme:
/// <list type = "bullet|number|table" >
/// <list type = "number" >
/// <listheader>
/// <item>1. Decode spectral data to Jpeg color space</item>
/// <item>2. Convert from Jpeg color space to RGB</item>
/// <item>3. Convert from RGB to target pixel space</item>
/// <item>Decode spectral data to Jpeg color space</item>
/// <item>Convert from Jpeg color space to RGB</item>
/// <item>Convert from RGB to target pixel space</item>
/// </listheader>
/// </list>
/// </remarks>
@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <summary>
/// Color converter from jpeg color space to target pixel color space.
/// </summary>
private JpegColorConverter colorConverter;
private JpegColorConverterBase colorConverter;
/// <summary>
/// Intermediate buffer of RGB components used in color conversion.
@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
int y = yy - this.pixelRowCounter;
var values = new JpegColorConverter.ComponentValues(this.componentProcessors, y);
var values = new JpegColorConverterBase.ComponentValues(this.componentProcessors, y);
this.colorConverter.ConvertToRgbInplace(values);
values = values.Slice(0, width); // slice away Jpeg padding

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

@ -27,6 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
}
/// <inheritdoc/>
protected override JpegColorConverter GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverter.GetConverter(JpegColorSpace.RGB, frame.Precision);
protected override JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverterBase.GetConverter(JpegColorSpace.RGB, frame.Precision);
}
}

134
src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs

@ -40,11 +40,43 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Note: method name in libwebp reference implementation is called VP8SSE16x16.
[MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8_Sse16X16(Span<byte> a, Span<byte> b) => Vp8_SseNxN(a, b, 16, 16);
public static int Vp8_Sse16X16(Span<byte> a, Span<byte> b)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
return Vp8_Sse16xN_Avx2(a, b, 4);
}
if (Sse2.IsSupported)
{
return Vp8_Sse16xN_Sse2(a, b, 8);
}
#endif
{
return Vp8_SseNxN(a, b, 16, 16);
}
}
// Note: method name in libwebp reference implementation is called VP8SSE16x8.
[MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8_Sse16X8(Span<byte> a, Span<byte> b) => Vp8_SseNxN(a, b, 16, 8);
public static int Vp8_Sse16X8(Span<byte> a, Span<byte> b)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
return Vp8_Sse16xN_Avx2(a, b, 2);
}
if (Sse2.IsSupported)
{
return Vp8_Sse16xN_Sse2(a, b, 4);
}
#endif
{
return Vp8_SseNxN(a, b, 16, 8);
}
}
// Note: method name in libwebp reference implementation is called VP8SSE4x4.
[MethodImpl(InliningOptions.ShortMethod)]
@ -146,6 +178,104 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return count;
}
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(InliningOptions.ShortMethod)]
private static int Vp8_Sse16xN_Sse2(Span<byte> a, Span<byte> b, int numPairs)
{
Vector128<int> sum = Vector128<int>.Zero;
nint offset = 0;
ref byte aRef = ref MemoryMarshal.GetReference(a);
ref byte bRef = ref MemoryMarshal.GetReference(b);
for (int i = 0; i < numPairs; i++)
{
// Load values.
Vector128<byte> a0 = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref aRef, offset));
Vector128<byte> b0 = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref bRef, offset));
Vector128<byte> a1 = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref aRef, offset + WebpConstants.Bps));
Vector128<byte> b1 = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref bRef, offset + WebpConstants.Bps));
Vector128<int> sum1 = SubtractAndAccumulate(a0, b0);
Vector128<int> sum2 = SubtractAndAccumulate(a1, b1);
sum = Sse2.Add(sum, Sse2.Add(sum1, sum2));
offset += 2 * WebpConstants.Bps;
}
return Numerics.ReduceSum(sum);
}
[MethodImpl(InliningOptions.ShortMethod)]
private static int Vp8_Sse16xN_Avx2(Span<byte> a, Span<byte> b, int numPairs)
{
Vector256<int> sum = Vector256<int>.Zero;
nint offset = 0;
ref byte aRef = ref MemoryMarshal.GetReference(a);
ref byte bRef = ref MemoryMarshal.GetReference(b);
for (int i = 0; i < numPairs; i++)
{
// Load values.
var a0 = Vector256.Create(
Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref aRef, offset)),
Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref aRef, offset + WebpConstants.Bps)));
var b0 = Vector256.Create(
Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref bRef, offset)),
Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref bRef, offset + WebpConstants.Bps)));
var a1 = Vector256.Create(
Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref aRef, offset + (2 * WebpConstants.Bps))),
Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref aRef, offset + (3 * WebpConstants.Bps))));
var b1 = Vector256.Create(
Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref bRef, offset + (2 * WebpConstants.Bps))),
Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref bRef, offset + (3 * WebpConstants.Bps))));
Vector256<int> sum1 = SubtractAndAccumulate(a0, b0);
Vector256<int> sum2 = SubtractAndAccumulate(a1, b1);
sum = Avx2.Add(sum, Avx2.Add(sum1, sum2));
offset += 4 * WebpConstants.Bps;
}
return Numerics.ReduceSum(sum);
}
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector128<int> SubtractAndAccumulate(Vector128<byte> a, Vector128<byte> b)
{
// Take abs(a-b) in 8b.
Vector128<byte> ab = Sse2.SubtractSaturate(a, b);
Vector128<byte> ba = Sse2.SubtractSaturate(b, a);
Vector128<byte> absAb = Sse2.Or(ab, ba);
// Zero-extend to 16b.
Vector128<byte> c0 = Sse2.UnpackLow(absAb, Vector128<byte>.Zero);
Vector128<byte> c1 = Sse2.UnpackHigh(absAb, Vector128<byte>.Zero);
// Multiply with self.
Vector128<int> sum1 = Sse2.MultiplyAddAdjacent(c0.AsInt16(), c0.AsInt16());
Vector128<int> sum2 = Sse2.MultiplyAddAdjacent(c1.AsInt16(), c1.AsInt16());
return Sse2.Add(sum1, sum2);
}
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector256<int> SubtractAndAccumulate(Vector256<byte> a, Vector256<byte> b)
{
// Take abs(a-b) in 8b.
Vector256<byte> ab = Avx2.SubtractSaturate(a, b);
Vector256<byte> ba = Avx2.SubtractSaturate(b, a);
Vector256<byte> absAb = Avx2.Or(ab, ba);
// Zero-extend to 16b.
Vector256<byte> c0 = Avx2.UnpackLow(absAb, Vector256<byte>.Zero);
Vector256<byte> c1 = Avx2.UnpackHigh(absAb, Vector256<byte>.Zero);
// Multiply with self.
Vector256<int> sum1 = Avx2.MultiplyAddAdjacent(c0.AsInt16(), c0.AsInt16());
Vector256<int> sum2 = Avx2.MultiplyAddAdjacent(c1.AsInt16(), c1.AsInt16());
return Avx2.Add(sum1, sum2);
}
#endif
[MethodImpl(InliningOptions.ShortMethod)]
public static void Vp8Copy4X4(Span<byte> src, Span<byte> dst) => Copy(src, dst, 4, 4);

44
src/ImageSharp/Formats/Webp/Lossy/Vp8Histogram.cs

@ -3,6 +3,11 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
@ -19,6 +24,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// </summary>
private const int MaxCoeffThresh = 31;
#if SUPPORTS_RUNTIME_INTRINSICS
private static readonly Vector256<short> MaxCoeffThreshVec = Vector256.Create((short)MaxCoeffThresh);
#endif
private int maxValue;
private int lastNonZero;
@ -52,11 +61,38 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Vp8Encoding.FTransform(reference.Slice(WebpLookupTables.Vp8DspScan[j]), pred.Slice(WebpLookupTables.Vp8DspScan[j]), this.output, this.scratch);
// Convert coefficients to bin.
for (int k = 0; k < 16; ++k)
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
int v = Math.Abs(this.output[k]) >> 3;
int clippedValue = ClipMax(v, MaxCoeffThresh);
++this.distribution[clippedValue];
// Load.
ref short outputRef = ref MemoryMarshal.GetReference<short>(this.output);
Vector256<byte> out0 = Unsafe.As<short, Vector256<byte>>(ref outputRef);
// v = abs(out) >> 3
Vector256<ushort> abs0 = Avx2.Abs(out0.AsInt16());
Vector256<short> v0 = Avx2.ShiftRightArithmetic(abs0.AsInt16(), 3);
// bin = min(v, MAX_COEFF_THRESH)
Vector256<short> min0 = Avx2.Min(v0, MaxCoeffThreshVec);
// Store.
Unsafe.As<short, Vector256<short>>(ref outputRef) = min0;
// Convert coefficients to bin.
for (int k = 0; k < 16; ++k)
{
++this.distribution[this.output[k]];
}
}
else
#endif
{
for (int k = 0; k < 16; ++k)
{
int v = Math.Abs(this.output[k]) >> 3;
int clippedValue = ClipMax(v, MaxCoeffThresh);
++this.distribution[clippedValue];
}
}
}

16
tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs

@ -17,25 +17,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
[Benchmark(Baseline = true)]
public void Scalar()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromCmykBasic(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromCmykScalar(8).ConvertToRgbInplace(values);
}
[Benchmark]
public void SimdVector8()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromCmykVector8(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromCmykVector(8).ConvertToRgbInplace(values);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void SimdVectorAvx2()
public void SimdVectorAvx()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromCmykAvx2(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromCmykAvx(8).ConvertToRgbInplace(values);
}
#endif
}
}

14
tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using BenchmarkDotNet.Attributes;
@ -17,17 +17,19 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
[Benchmark(Baseline = true)]
public void Scalar()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromGrayscaleBasic(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromGrayscaleScalar(8).ConvertToRgbInplace(values);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void SimdVectorAvx2()
public void SimdVectorAvx()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromGrayscaleAvx2(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromGrayscaleAvx(8).ConvertToRgbInplace(values);
}
#endif
}
}

18
tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using BenchmarkDotNet.Attributes;
@ -17,25 +17,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
[Benchmark(Baseline = true)]
public void Scalar()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromRgbBasic(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromRgbScalar(8).ConvertToRgbInplace(values);
}
[Benchmark]
public void SimdVector8()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromRgbVector8(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromRgbVector(8).ConvertToRgbInplace(values);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void SimdVectorAvx2()
public void SimdVectorAvx()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromRgbAvx2(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromRgbAvx(8).ConvertToRgbInplace(values);
}
#endif
}
}

24
tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs

@ -17,33 +17,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
[Benchmark]
public void Scalar()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYCbCrBasic(8).ConvertToRgbInplace(values);
}
[Benchmark(Baseline = true)]
public void SimdVector()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYCbCrVector4(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromYCbCrScalar(8).ConvertToRgbInplace(values);
}
[Benchmark]
public void SimdVector8()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYCbCrVector8(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromYCbCrVector(8).ConvertToRgbInplace(values);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void SimdVectorAvx2()
public void SimdVectorAvx()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYCbCrAvx2(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromYCbCrAvx(8).ConvertToRgbInplace(values);
}
#endif
}
}

16
tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using BenchmarkDotNet.Attributes;
@ -17,25 +17,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
[Benchmark(Baseline = true)]
public void Scalar()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYccKBasic(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromYccKScalar(8).ConvertToRgbInplace(values);
}
[Benchmark]
public void SimdVector8()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYccKVector8(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromYccKVector(8).ConvertToRgbInplace(values);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void SimdVectorAvx2()
{
var values = new JpegColorConverter.ComponentValues(this.Input, 0);
var values = new JpegColorConverterBase.ComponentValues(this.Input, 0);
new JpegColorConverter.FromYccKAvx2(8).ConvertToRgbInplace(values);
new JpegColorConverterBase.FromYccKAvx(8).ConvertToRgbInplace(values);
}
#endif
}
}

1
tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs

@ -19,7 +19,6 @@ using static SixLabors.ImageSharp.Tests.TestImages.Bmp;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Bmp
{
[Collection("RunSerial")]
[Trait("Format", "Bmp")]
public class BmpDecoderTests
{

7
tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs

@ -17,19 +17,18 @@ using static SixLabors.ImageSharp.Tests.TestImages.Bmp;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Bmp
{
[Collection("RunSerial")]
[Trait("Format", "Bmp")]
public class BmpEncoderTests
{
public static readonly TheoryData<BmpBitsPerPixel> BitsPerPixel =
new TheoryData<BmpBitsPerPixel>
new()
{
BmpBitsPerPixel.Pixel24,
BmpBitsPerPixel.Pixel32
};
public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles =
new TheoryData<string, int, int, PixelResolutionUnit>
new()
{
{ Car, 3780, 3780, PixelResolutionUnit.PixelsPerMeter },
{ V5Header, 3780, 3780, PixelResolutionUnit.PixelsPerMeter },
@ -37,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
};
public static readonly TheoryData<string, BmpBitsPerPixel> BmpBitsPerPixelFiles =
new TheoryData<string, BmpBitsPerPixel>
new()
{
{ Bit1, BmpBitsPerPixel.Pixel1 },
{ Bit4, BmpBitsPerPixel.Pixel4 },

8
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -8,7 +8,6 @@ using System.Linq;
using System.Reflection;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -16,7 +15,6 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats
{
[Collection("RunSerial")]
public class GeneralFormatTests
{
/// <summary>
@ -34,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Formats
/// <summary>
/// The collection of image files to test against.
/// </summary>
protected static readonly List<TestFile> Files = new List<TestFile>
protected static readonly List<TestFile> Files = new()
{
TestFile.Create(TestImages.Jpeg.Baseline.Calliphora),
TestFile.Create(TestImages.Bmp.Car),
@ -85,8 +83,8 @@ namespace SixLabors.ImageSharp.Tests.Formats
}
public static readonly TheoryData<string> QuantizerNames =
new TheoryData<string>
{
new()
{
nameof(KnownQuantizers.Octree),
nameof(KnownQuantizers.WebSafe),
nameof(KnownQuantizers.Werner),

3
tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs

@ -17,13 +17,12 @@ using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Gif
{
[Collection("RunSerial")]
[Trait("Format", "Gif")]
public class GifDecoderTests
{
private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32;
private static GifDecoder GifDecoder => new GifDecoder();
private static GifDecoder GifDecoder => new();
public static readonly string[] MultiFrameTestFiles =
{

3
tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs

@ -13,7 +13,6 @@ using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Gif
{
[Collection("RunSerial")]
[Trait("Format", "Gif")]
public class GifEncoderTests
{
@ -21,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0015F);
public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles =
new TheoryData<string, int, int, PixelResolutionUnit>
new()
{
{ TestImages.Gif.Rings, (int)ImageMetadata.DefaultHorizontalResolution, (int)ImageMetadata.DefaultVerticalResolution, PixelResolutionUnit.PixelsPerInch },
{ TestImages.Gif.Ratio1x4, 1, 4, PixelResolutionUnit.AspectRatio },

5
tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs

@ -12,12 +12,11 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Gif
{
[Collection("RunSerial")]
[Trait("Format", "Gif")]
public class GifMetadataTests
{
public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles =
new TheoryData<string, int, int, PixelResolutionUnit>
new()
{
{ TestImages.Gif.Rings, (int)ImageMetadata.DefaultHorizontalResolution, (int)ImageMetadata.DefaultVerticalResolution, PixelResolutionUnit.PixelsPerInch },
{ TestImages.Gif.Ratio1x4, 1, 4, PixelResolutionUnit.AspectRatio },
@ -25,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
};
public static readonly TheoryData<string, uint> RepeatFiles =
new TheoryData<string, uint>
new()
{
{ TestImages.Gif.Cheers, 0 },
{ TestImages.Gif.Receipt, 1 },

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

@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Tests.Colorspaces.Conversion;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@ -18,20 +18,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Trait("Format", "Jpg")]
public class JpegColorConverterTests
{
private const float MaxColorChannelValue = 255f;
private const float Precision = 0.1F / 255;
private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(Precision);
private const int TestBufferLength = 40;
// int inputBufferLength, int resultBufferLength, int seed
public static readonly TheoryData<int, int, int> CommonConversionData =
new TheoryData<int, int, int>
{
{ 40, 40, 1 },
{ 42, 40, 2 },
{ 42, 39, 3 }
};
#if SUPPORTS_RUNTIME_INTRINSICS
private static readonly HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX;
#else
private static readonly HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll;
#endif
private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new(epsilon: Precision);
private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter();
private static readonly ColorSpaceConverter ColorSpaceConverter = new();
public static readonly TheoryData<int> Seeds = new() { 1, 2, 3 };
public JpegColorConverterTests(ITestOutputHelper output)
{
@ -40,323 +43,265 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
private ITestOutputHelper Output { get; }
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromYCbCrBasic(int inputBufferLength, int resultBufferLength, int seed)
[Fact]
public void GetConverterThrowsExceptionOnInvalidColorSpace()
{
ValidateConversion(
new JpegColorConverter.FromYCbCrBasic(8),
3,
inputBufferLength,
resultBufferLength,
seed);
Assert.Throws<Exception>(() => JpegColorConverterBase.GetConverter(JpegColorSpace.Undefined, 8));
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromYCbCrVector4(int inputBufferLength, int resultBufferLength, int seed)
[Fact]
public void GetConverterThrowsExceptionOnInvalidPrecision()
{
if (!SimdUtils.HasVector4)
{
this.Output.WriteLine("No SSE present, skipping test!");
return;
}
ValidateConversion(
new JpegColorConverter.FromYCbCrVector4(8),
3,
inputBufferLength,
resultBufferLength,
seed);
// Valid precisions: 8 & 12 bit
Assert.Throws<Exception>(() => JpegColorConverterBase.GetConverter(JpegColorSpace.YCbCr, 9));
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromYCbCrVector8(int inputBufferLength, int resultBufferLength, int seed)
[InlineData(JpegColorSpace.Grayscale, 8)]
[InlineData(JpegColorSpace.Grayscale, 12)]
[InlineData(JpegColorSpace.Ycck, 8)]
[InlineData(JpegColorSpace.Ycck, 12)]
[InlineData(JpegColorSpace.Cmyk, 8)]
[InlineData(JpegColorSpace.Cmyk, 12)]
[InlineData(JpegColorSpace.RGB, 8)]
[InlineData(JpegColorSpace.RGB, 12)]
[InlineData(JpegColorSpace.YCbCr, 8)]
[InlineData(JpegColorSpace.YCbCr, 12)]
internal void GetConverterReturnsValidConverter(JpegColorSpace colorSpace, int precision)
{
if (!SimdUtils.HasVector8)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
return;
}
var converter = JpegColorConverterBase.GetConverter(colorSpace, precision);
ValidateConversion(
new JpegColorConverter.FromYCbCrVector8(8),
3,
inputBufferLength,
resultBufferLength,
seed);
Assert.NotNull(converter);
Assert.True(converter.IsAvailable);
Assert.Equal(colorSpace, converter.ColorSpace);
Assert.Equal(precision, converter.Precision);
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromYCbCrAvx2(int inputBufferLength, int resultBufferLength, int seed)
[InlineData(JpegColorSpace.Grayscale, 1)]
[InlineData(JpegColorSpace.Ycck, 4)]
[InlineData(JpegColorSpace.Cmyk, 4)]
[InlineData(JpegColorSpace.RGB, 3)]
[InlineData(JpegColorSpace.YCbCr, 3)]
internal void ConvertWithSelectedConverter(JpegColorSpace colorSpace, int componentCount)
{
if (!SimdUtils.HasAvx2)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
return;
}
var converter = JpegColorConverterBase.GetConverter(colorSpace, 8);
ValidateConversion(
new JpegColorConverter.FromYCbCrAvx2(8),
3,
inputBufferLength,
resultBufferLength,
seed);
converter,
componentCount,
1);
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromYCbCr_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
JpegColorSpace.YCbCr,
3,
inputBufferLength,
resultBufferLength,
seed);
}
[MemberData(nameof(Seeds))]
public void FromYCbCrBasic(int seed) =>
this.TestConverter(new JpegColorConverterBase.FromYCbCrScalar(8), 3, seed);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromCmykBasic(int inputBufferLength, int resultBufferLength, int seed)
[MemberData(nameof(Seeds))]
public void FromYCbCrVector(int seed)
{
ValidateConversion(
new JpegColorConverter.FromCmykBasic(8),
4,
inputBufferLength,
resultBufferLength,
seed);
}
var converter = new JpegColorConverterBase.FromYCbCrVector(8);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromCmykVector8(int inputBufferLength, int resultBufferLength, int seed)
{
if (!SimdUtils.HasVector8)
if (!converter.IsAvailable)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
this.Output.WriteLine(
$"Skipping test - {converter.GetType().Name} is not supported on current hardware.");
return;
}
ValidateConversion(
new JpegColorConverter.FromCmykVector8(8),
4,
inputBufferLength,
resultBufferLength,
seed);
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
seed,
IntrinsicsConfig);
static void RunTest(string arg) =>
ValidateConversion(
new JpegColorConverterBase.FromYCbCrVector(8),
3,
FeatureTestRunner.Deserialize<int>(arg));
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromCmykAvx2(int inputBufferLength, int resultBufferLength, int seed)
[MemberData(nameof(Seeds))]
public void FromCmykBasic(int seed) =>
this.TestConverter(new JpegColorConverterBase.FromCmykScalar(8), 4, seed);
[Theory]
[MemberData(nameof(Seeds))]
public void FromCmykVector(int seed)
{
if (!SimdUtils.HasAvx2)
var converter = new JpegColorConverterBase.FromCmykVector(8);
if (!converter.IsAvailable)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
this.Output.WriteLine(
$"Skipping test - {converter.GetType().Name} is not supported on current hardware.");
return;
}
ValidateConversion(
new JpegColorConverter.FromCmykAvx2(8),
4,
inputBufferLength,
resultBufferLength,
seed);
}
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
seed,
IntrinsicsConfig);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromCmyk_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
JpegColorSpace.Cmyk,
4,
inputBufferLength,
resultBufferLength,
seed);
static void RunTest(string arg) =>
ValidateConversion(
new JpegColorConverterBase.FromCmykVector(8),
4,
FeatureTestRunner.Deserialize<int>(arg));
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromGrayscaleBasic(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
new JpegColorConverter.FromGrayscaleBasic(8),
1,
inputBufferLength,
resultBufferLength,
seed);
}
[MemberData(nameof(Seeds))]
public void FromGrayscaleBasic(int seed) =>
this.TestConverter(new JpegColorConverterBase.FromGrayscaleScalar(8), 1, seed);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromGrayscaleAvx2(int inputBufferLength, int resultBufferLength, int seed)
[MemberData(nameof(Seeds))]
public void FromGrayscaleVector(int seed)
{
if (!SimdUtils.HasAvx2)
var converter = new JpegColorConverterBase.FromGrayScaleVector(8);
if (!converter.IsAvailable)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
this.Output.WriteLine(
$"Skipping test - {converter.GetType().Name} is not supported on current hardware.");
return;
}
ValidateConversion(
new JpegColorConverter.FromGrayscaleAvx2(8),
1,
inputBufferLength,
resultBufferLength,
seed);
}
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
seed,
IntrinsicsConfig);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromGraysacle_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
JpegColorSpace.Grayscale,
1,
inputBufferLength,
resultBufferLength,
seed);
static void RunTest(string arg) =>
ValidateConversion(
new JpegColorConverterBase.FromGrayScaleVector(8),
1,
FeatureTestRunner.Deserialize<int>(arg));
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromRgbBasic(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
new JpegColorConverter.FromRgbBasic(8),
3,
inputBufferLength,
resultBufferLength,
seed);
}
[MemberData(nameof(Seeds))]
public void FromRgbBasic(int seed) =>
this.TestConverter(new JpegColorConverterBase.FromRgbScalar(8), 3, seed);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromRgbVector8(int inputBufferLength, int resultBufferLength, int seed)
[MemberData(nameof(Seeds))]
public void FromRgbVector(int seed)
{
if (!SimdUtils.HasVector8)
var converter = new JpegColorConverterBase.FromRgbVector(8);
if (!converter.IsAvailable)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
this.Output.WriteLine(
$"Skipping test - {converter.GetType().Name} is not supported on current hardware.");
return;
}
ValidateConversion(
new JpegColorConverter.FromRgbVector8(8),
3,
inputBufferLength,
resultBufferLength,
seed);
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
seed,
IntrinsicsConfig);
static void RunTest(string arg) =>
ValidateConversion(
new JpegColorConverterBase.FromRgbVector(8),
3,
FeatureTestRunner.Deserialize<int>(arg));
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromRgbAvx2(int inputBufferLength, int resultBufferLength, int seed)
[MemberData(nameof(Seeds))]
public void FromYccKBasic(int seed) =>
this.TestConverter(new JpegColorConverterBase.FromYccKScalar(8), 4, seed);
[Theory]
[MemberData(nameof(Seeds))]
public void FromYccKVector(int seed)
{
if (!SimdUtils.HasAvx2)
var converter = new JpegColorConverterBase.FromYccKVector(8);
if (!converter.IsAvailable)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
this.Output.WriteLine(
$"Skipping test - {converter.GetType().Name} is not supported on current hardware.");
return;
}
ValidateConversion(
new JpegColorConverter.FromRgbAvx2(8),
3,
inputBufferLength,
resultBufferLength,
seed);
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
seed,
IntrinsicsConfig);
static void RunTest(string arg) =>
ValidateConversion(
new JpegColorConverterBase.FromYccKVector(8),
4,
FeatureTestRunner.Deserialize<int>(arg));
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromRgb_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
JpegColorSpace.RGB,
3,
inputBufferLength,
resultBufferLength,
seed);
}
[MemberData(nameof(Seeds))]
public void FromYCbCrAvx2(int seed) =>
this.TestConverter(new JpegColorConverterBase.FromYCbCrAvx(8), 3, seed);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromYccKBasic(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
new JpegColorConverter.FromYccKBasic(8),
4,
inputBufferLength,
resultBufferLength,
seed);
}
[MemberData(nameof(Seeds))]
public void FromCmykAvx2(int seed) =>
this.TestConverter(new JpegColorConverterBase.FromCmykAvx(8), 4, seed);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromYccKVector8(int inputBufferLength, int resultBufferLength, int seed)
{
if (!SimdUtils.HasVector8)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
return;
}
[MemberData(nameof(Seeds))]
public void FromGrayscaleAvx2(int seed) =>
this.TestConverter(new JpegColorConverterBase.FromGrayscaleAvx(8), 1, seed);
ValidateConversion(
new JpegColorConverter.FromYccKVector8(8),
4,
inputBufferLength,
resultBufferLength,
seed);
}
[Theory]
[MemberData(nameof(Seeds))]
public void FromRgbAvx2(int seed) =>
this.TestConverter(new JpegColorConverterBase.FromRgbAvx(8), 3, seed);
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromYccKAvx2(int inputBufferLength, int resultBufferLength, int seed)
[MemberData(nameof(Seeds))]
public void FromYccKAvx2(int seed) =>
this.TestConverter(new JpegColorConverterBase.FromYccKAvx(8), 4, seed);
#endif
private void TestConverter(
JpegColorConverterBase converter,
int componentCount,
int seed)
{
if (!SimdUtils.HasAvx2)
if (!converter.IsAvailable)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
this.Output.WriteLine(
$"Skipping test - {converter.GetType().Name} is not supported on current hardware.");
return;
}
ValidateConversion(
new JpegColorConverter.FromYccKAvx2(8),
4,
inputBufferLength,
resultBufferLength,
seed);
}
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromYcck_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)
{
ValidateConversion(
JpegColorSpace.Ycck,
4,
inputBufferLength,
resultBufferLength,
converter,
componentCount,
seed);
}
private static JpegColorConverter.ComponentValues CreateRandomValues(
private static JpegColorConverterBase.ComponentValues CreateRandomValues(
int length,
int componentCount,
int inputBufferLength,
int seed,
float minVal = 0f,
float maxVal = 255f)
int seed)
{
var rnd = new Random(seed);
var buffers = new Buffer2D<float>[componentCount];
for (int i = 0; i < componentCount; i++)
{
var values = new float[inputBufferLength];
float[] values = new float[length];
for (int j = 0; j < inputBufferLength; j++)
for (int j = 0; j < values.Length; j++)
{
values[j] = ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal;
values[j] = (float)rnd.NextDouble() * MaxColorChannelValue;
}
// no need to dispose when buffer is not array owner
@ -365,55 +310,34 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
buffers[i] = new Buffer2D<float>(source, values.Length, 1);
}
return new JpegColorConverter.ComponentValues(buffers, 0);
}
private static void ValidateConversion(
JpegColorSpace colorSpace,
int componentCount,
int inputBufferLength,
int resultBufferLength,
int seed)
{
ValidateConversion(
JpegColorConverter.GetConverter(colorSpace, 8),
componentCount,
inputBufferLength,
resultBufferLength,
seed);
return new JpegColorConverterBase.ComponentValues(buffers, 0);
}
private static void ValidateConversion(
JpegColorConverter converter,
JpegColorConverterBase converter,
int componentCount,
int inputBufferLength,
int resultBufferLength,
int seed)
{
JpegColorConverter.ComponentValues original = CreateRandomValues(componentCount, inputBufferLength, seed);
JpegColorConverter.ComponentValues values = Copy(original);
JpegColorConverterBase.ComponentValues original = CreateRandomValues(TestBufferLength, componentCount, seed);
JpegColorConverterBase.ComponentValues values = new(
original.ComponentCount,
original.Component0.ToArray(),
original.Component1.ToArray(),
original.Component2.ToArray(),
original.Component3.ToArray());
converter.ConvertToRgbInplace(values);
for (int i = 0; i < resultBufferLength; i++)
for (int i = 0; i < TestBufferLength; 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(
JpegColorSpace colorSpace,
in JpegColorConverter.ComponentValues original,
in JpegColorConverter.ComponentValues result,
in JpegColorConverterBase.ComponentValues original,
in JpegColorConverterBase.ComponentValues result,
int i)
{
switch (colorSpace)
@ -433,92 +357,90 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
case JpegColorSpace.YCbCr:
ValidateYCbCr(original, result, i);
break;
case JpegColorSpace.Undefined:
default:
Assert.True(false, $"Colorspace {colorSpace} not supported!");
Assert.True(false, $"Invalid Colorspace enum value: {colorSpace}.");
break;
}
}
private static void ValidateYCbCr(in JpegColorConverter.ComponentValues values, in JpegColorConverter.ComponentValues result, int i)
private static void ValidateYCbCr(in JpegColorConverterBase.ComponentValues values, in JpegColorConverterBase.ComponentValues result, int i)
{
float y = values.Component0[i];
float cb = values.Component1[i];
float cr = values.Component2[i];
var ycbcr = new YCbCr(y, cb, cr);
var expected = ColorSpaceConverter.ToRgb(new YCbCr(y, cb, cr));
var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]);
var expected = ColorSpaceConverter.ToRgb(ycbcr);
Assert.Equal(expected, actual, ColorSpaceComparer);
bool equal = ColorSpaceComparer.Equals(expected, actual);
Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}");
}
private static void ValidateCyyK(in JpegColorConverter.ComponentValues values, in JpegColorConverter.ComponentValues result, int i)
private static void ValidateCyyK(in JpegColorConverterBase.ComponentValues values, in JpegColorConverterBase.ComponentValues result, int i)
{
var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
float y = values.Component0[i];
float cb = values.Component1[i] - 128F;
float cr = values.Component2[i] - 128F;
float k = values.Component3[i] / 255F;
v.X = (255F - (float)Math.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k;
v.Y = (255F - (float)Math.Round(
float r = (255F - (float)Math.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k;
float g = (255F - (float)Math.Round(
y - (0.344136F * cb) - (0.714136F * cr),
MidpointRounding.AwayFromZero)) * k;
v.Z = (255F - (float)Math.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
v.W = 1F;
float b = (255F - (float)Math.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
v *= scale;
r /= MaxColorChannelValue;
g /= MaxColorChannelValue;
b /= MaxColorChannelValue;
var expected = new Rgb(r, g, b);
var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]);
var expected = new Rgb(v.X, v.Y, v.Z);
Assert.Equal(expected, actual, ColorSpaceComparer);
bool equal = ColorSpaceComparer.Equals(expected, actual);
Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}");
}
private static void ValidateRgb(in JpegColorConverter.ComponentValues values, in JpegColorConverter.ComponentValues result, int i)
private static void ValidateRgb(in JpegColorConverterBase.ComponentValues values, in JpegColorConverterBase.ComponentValues result, int i)
{
float r = values.Component0[i];
float g = values.Component1[i];
float b = values.Component2[i];
float r = values.Component0[i] / MaxColorChannelValue;
float g = values.Component1[i] / MaxColorChannelValue;
float b = values.Component2[i] / MaxColorChannelValue;
var expected = new Rgb(r, g, b);
var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]);
var expected = new Rgb(r / 255F, g / 255F, b / 255F);
Assert.Equal(expected, actual, ColorSpaceComparer);
bool equal = ColorSpaceComparer.Equals(expected, actual);
Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}");
}
private static void ValidateGrayScale(in JpegColorConverter.ComponentValues values, in JpegColorConverter.ComponentValues result, int i)
private static void ValidateGrayScale(in JpegColorConverterBase.ComponentValues values, in JpegColorConverterBase.ComponentValues result, int i)
{
float y = values.Component0[i];
float y = values.Component0[i] / MaxColorChannelValue;
var expected = new Rgb(y, y, y);
var actual = new Rgb(result.Component0[i], result.Component0[i], result.Component0[i]);
var expected = new Rgb(y / 255F, y / 255F, y / 255F);
Assert.Equal(expected, actual, ColorSpaceComparer);
bool equal = ColorSpaceComparer.Equals(expected, actual);
Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}");
}
private static void ValidateCmyk(in JpegColorConverter.ComponentValues values, in JpegColorConverter.ComponentValues result, int i)
private static void ValidateCmyk(in JpegColorConverterBase.ComponentValues values, in JpegColorConverterBase.ComponentValues result, int i)
{
var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
float c = values.Component0[i];
float m = values.Component1[i];
float y = values.Component2[i];
float k = values.Component3[i] / 255F;
v.X = c * k;
v.Y = m * k;
v.Z = y * k;
v.W = 1F;
float k = values.Component3[i] / MaxColorChannelValue;
v *= scale;
float r = c * k / MaxColorChannelValue;
float g = m * k / MaxColorChannelValue;
float b = y * k / MaxColorChannelValue;
var expected = new Rgb(r, g, b);
var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]);
var expected = new Rgb(v.X, v.Y, v.Z);
Assert.Equal(expected, actual, ColorSpaceComparer);
bool equal = ColorSpaceComparer.Equals(expected, actual);
Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}");
}
}
}

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

@ -21,8 +21,7 @@ using Xunit.Abstractions;
namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
// TODO: Scatter test cases into multiple test classes
[Collection("RunSerial")]
[Trait("Format", "Jpg")]
[Trait("Format", "Jpg")]
public partial class JpegDecoderTests
{
public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.Bgr24 | PixelTypes.RgbaVector;

13
tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs

@ -19,23 +19,22 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
[Collection("RunSerial")]
[Trait("Format", "Jpg")]
public class JpegEncoderTests
{
private static JpegEncoder JpegEncoder => new JpegEncoder();
private static JpegEncoder JpegEncoder => new();
private static JpegDecoder JpegDecoder => new JpegDecoder();
private static JpegDecoder JpegDecoder => new();
public static readonly TheoryData<string, int> QualityFiles =
new TheoryData<string, int>
new()
{
{ TestImages.Jpeg.Baseline.Calliphora, 80 },
{ TestImages.Jpeg.Progressive.Fb, 75 }
};
public static readonly TheoryData<JpegColorType, int> BitsPerPixel_Quality =
new TheoryData<JpegColorType, int>
new()
{
{ JpegColorType.YCbCrRatio420, 40 },
{ JpegColorType.YCbCrRatio420, 60 },
@ -49,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
};
public static readonly TheoryData<int> Grayscale_Quality =
new TheoryData<int>
new()
{
{ 40 },
{ 60 },
@ -57,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
};
public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles =
new TheoryData<string, int, int, PixelResolutionUnit>
new()
{
{ TestImages.Jpeg.Baseline.Ratio1x1, 1, 1, PixelResolutionUnit.AspectRatio },
{ TestImages.Jpeg.Baseline.Snake, 300, 300, PixelResolutionUnit.PixelsPerInch },

1
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

@ -18,7 +18,6 @@ using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Png
{
[Collection("RunSerial")]
[Trait("Format", "Png")]
public partial class PngDecoderTests
{

19
tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

@ -15,21 +15,20 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Png
{
[Collection("RunSerial")]
[Trait("Format", "Png")]
public partial class PngEncoderTests
{
private static PngEncoder PngEncoder => new PngEncoder();
private static PngEncoder PngEncoder => new();
public static readonly TheoryData<string, PngBitDepth> PngBitDepthFiles =
new TheoryData<string, PngBitDepth>
new()
{
{ TestImages.Png.Rgb48Bpp, PngBitDepth.Bit16 },
{ TestImages.Png.Bpp1, PngBitDepth.Bit1 }
};
public static readonly TheoryData<string, PngBitDepth, PngColorType> PngTrnsFiles =
new TheoryData<string, PngBitDepth, PngColorType>
new()
{
{ TestImages.Png.Gray1BitTrans, PngBitDepth.Bit1, PngColorType.Grayscale },
{ TestImages.Png.Gray2BitTrans, PngBitDepth.Bit2, PngColorType.Grayscale },
@ -43,7 +42,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
/// <summary>
/// All types except Palette
/// </summary>
public static readonly TheoryData<PngColorType> PngColorTypes = new TheoryData<PngColorType>
public static readonly TheoryData<PngColorType> PngColorTypes = new()
{
PngColorType.RgbWithAlpha,
PngColorType.Rgb,
@ -51,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
PngColorType.GrayscaleWithAlpha,
};
public static readonly TheoryData<PngFilterMethod> PngFilterMethods = new TheoryData<PngFilterMethod>
public static readonly TheoryData<PngFilterMethod> PngFilterMethods = new()
{
PngFilterMethod.None,
PngFilterMethod.Sub,
@ -65,7 +64,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
/// All types except Palette
/// </summary>
public static readonly TheoryData<PngCompressionLevel> CompressionLevels
= new TheoryData<PngCompressionLevel>
= new()
{
PngCompressionLevel.Level0,
PngCompressionLevel.Level1,
@ -79,12 +78,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
PngCompressionLevel.Level9,
};
public static readonly TheoryData<int> PaletteSizes = new TheoryData<int>
public static readonly TheoryData<int> PaletteSizes = new()
{
30, 55, 100, 201, 255
};
public static readonly TheoryData<int> PaletteLargeOnly = new TheoryData<int>
public static readonly TheoryData<int> PaletteLargeOnly = new()
{
80, 100, 120, 230
};
@ -96,7 +95,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
};
public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles =
new TheoryData<string, int, int, PixelResolutionUnit>
new()
{
{ TestImages.Png.Splash, 11810, 11810, PixelResolutionUnit.PixelsPerMeter },
{ TestImages.Png.Ratio1x4, 1, 4, PixelResolutionUnit.AspectRatio },

3
tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs

@ -15,11 +15,10 @@ using static SixLabors.ImageSharp.Tests.TestImages.Tga;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Tga
{
[Collection("RunSerial")]
[Trait("Format", "Tga")]
public class TgaDecoderTests
{
private static TgaDecoder TgaDecoder => new TgaDecoder();
private static TgaDecoder TgaDecoder => new();
[Theory]
[WithFile(Gray8BitTopLeft, PixelTypes.Rgba32)]

5
tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs

@ -11,19 +11,18 @@ using static SixLabors.ImageSharp.Tests.TestImages.Tga;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Tga
{
[Collection("RunSerial")]
[Trait("Format", "Tga")]
public class TgaEncoderTests
{
public static readonly TheoryData<TgaBitsPerPixel> BitsPerPixel =
new TheoryData<TgaBitsPerPixel>
new()
{
TgaBitsPerPixel.Pixel24,
TgaBitsPerPixel.Pixel32
};
public static readonly TheoryData<string, TgaBitsPerPixel> TgaBitsPerPixelFiles =
new TheoryData<string, TgaBitsPerPixel>
new()
{
{ Gray8BitBottomLeft, TgaBitsPerPixel.Pixel8 },
{ Bit16BottomLeft, TgaBitsPerPixel.Pixel16 },

5
tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

@ -15,15 +15,14 @@ using static SixLabors.ImageSharp.Tests.TestImages.Tiff;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
[Collection("RunSerial")]
[Trait("Format", "Tiff")]
public class TiffDecoderTests
{
public static readonly string[] MultiframeTestImages = Multiframes;
private static TiffDecoder TiffDecoder => new TiffDecoder();
private static TiffDecoder TiffDecoder => new();
private static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder();
private static MagickReferenceDecoder ReferenceDecoder => new();
[Theory]
[WithFile(RgbUncompressedTiled, PixelTypes.Rgba32)]

1
tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs

@ -11,7 +11,6 @@ using static SixLabors.ImageSharp.Tests.TestImages.Tiff;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
[Collection("RunSerial")]
[Trait("Format", "Tiff")]
public class TiffEncoderTests : TiffEncoderBaseTester
{

3
tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs

@ -17,11 +17,10 @@ using static SixLabors.ImageSharp.Tests.TestImages.Tiff;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
[Collection("RunSerial")]
[Trait("Format", "Tiff")]
public class TiffMetadataTests
{
private static TiffDecoder TiffDecoder => new TiffDecoder();
private static TiffDecoder TiffDecoder => new();
private class NumberComparer : IEqualityComparer<Number>
{

142
tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs

@ -76,6 +76,124 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
Assert.True(expected.SequenceEqual(dst));
}
private static void RunVp8Sse16X16Test()
{
// arrange
byte[] a =
{
154, 154, 151, 151, 149, 148, 151, 157, 163, 163, 154, 132, 102, 98, 104, 108, 107, 104, 104, 103,
101, 106, 123, 119, 170, 171, 172, 171, 168, 175, 171, 173, 151, 151, 149, 150, 147, 147, 146, 159,
164, 165, 154, 129, 92, 90, 101, 105, 104, 103, 104, 101, 100, 105, 123, 117, 172, 172, 172, 168,
170, 177, 170, 175, 151, 149, 150, 150, 147, 147, 156, 161, 161, 161, 151, 126, 93, 90, 102, 107,
104, 103, 104, 101, 104, 104, 122, 117, 172, 172, 170, 168, 170, 177, 172, 175, 150, 149, 152, 151,
148, 151, 160, 159, 157, 157, 148, 133, 96, 90, 103, 107, 104, 104, 101, 100, 102, 102, 121, 117,
170, 170, 169, 171, 171, 179, 173, 175, 149, 151, 152, 151, 148, 154, 162, 157, 154, 154, 151, 132,
92, 89, 101, 108, 104, 102, 101, 101, 103, 103, 123, 118, 171, 168, 177, 173, 171, 178, 172, 176,
152, 152, 152, 151, 154, 162, 161, 155, 149, 157, 156, 129, 92, 87, 101, 107, 102, 100, 107, 100,
101, 102, 123, 118, 170, 175, 182, 172, 171, 179, 173, 175, 152, 151, 154, 155, 160, 162, 161, 153,
150, 156, 153, 129, 92, 91, 102, 106, 100, 109, 115, 99, 101, 102, 124, 120, 171, 179, 178, 172,
171, 181, 171, 173, 154, 154, 154, 162, 160, 158, 156, 152, 153, 157, 151, 128, 86, 86, 102, 105,
102, 122, 114, 99, 101, 102, 125, 120, 178, 173, 177, 172, 171, 180, 172, 173, 154, 152, 158, 163,
150, 148, 148, 156, 151, 158, 152, 129, 87, 87, 101, 105, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 204, 154, 151, 165, 156, 141, 137, 146, 158, 152, 159, 152, 133,
90, 88, 99, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
154, 160, 164, 150, 126, 127, 149, 159, 155, 161, 153, 131, 84, 86, 97, 103, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 157, 167, 157, 137, 102, 128, 155, 161,
157, 159, 154, 134, 84, 82, 97, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 163, 163, 150, 113, 78, 132, 156, 162, 159, 160, 154, 132, 83, 78, 91, 97, 204,
204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 163, 157, 137, 80, 78,
131, 154, 163, 157, 159, 149, 131, 82, 77, 94, 100, 204, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 159, 151, 108, 72, 88, 132, 156, 162, 159, 157, 151, 130, 79, 78,
95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 151, 130,
82, 82, 89, 134, 154, 161, 161, 157, 152, 129, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 204, 204
};
byte[] b =
{
150, 150, 150, 150, 146, 149, 152, 154, 164, 166, 154, 132, 99, 92, 106, 112, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 150, 150, 150, 150, 146, 149, 152, 154,
161, 164, 151, 130, 93, 86, 100, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 150, 150, 150, 150, 146, 149, 152, 154, 158, 161, 148, 127, 93, 86, 100, 106,
204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 150, 150, 150, 150,
146, 149, 152, 154, 156, 159, 146, 125, 99, 92, 106, 112, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 204, 148, 148, 148, 148, 149, 158, 162, 159, 155, 155, 153, 129,
94, 87, 101, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
151, 151, 151, 151, 152, 159, 161, 156, 155, 155, 153, 129, 94, 87, 101, 106, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 154, 154, 154, 154, 156, 161, 159, 152,
155, 155, 153, 129, 94, 87, 101, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 156, 156, 156, 156, 159, 162, 158, 149, 155, 155, 153, 129, 94, 87, 101, 106,
204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 152, 153, 157, 162,
150, 149, 149, 151, 155, 160, 150, 131, 91, 90, 104, 104, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 204, 152, 156, 158, 157, 140, 137, 145, 159, 155, 160, 150, 131,
89, 88, 102, 101, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
153, 161, 160, 149, 118, 128, 147, 162, 155, 160, 150, 131, 86, 85, 99, 98, 204, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 154, 165, 161, 144, 96, 128, 154, 159, 155,
160, 150, 131, 83, 82, 97, 96, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 161, 160, 149, 105, 78, 127, 156, 170, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 160, 160, 133, 85, 81, 129, 155,
167, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 204, 156, 147, 109, 76, 85, 130, 153, 163, 156, 156, 154, 130, 81, 77, 95, 102,
204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 152, 128, 87, 83,
88, 132, 152, 159, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204
};
int expected = 2063;
// act
int actual = LossyUtils.Vp8_Sse16X16(a, b);
// assert
Assert.Equal(expected, actual);
}
private static void RunVp8Sse16X8Test()
{
// arrange
byte[] a =
{
107, 104, 104, 103, 101, 106, 123, 119, 170, 171, 172, 171, 168, 175, 171, 173, 151, 151, 149, 150,
147, 147, 146, 159, 164, 165, 154, 129, 92, 90, 101, 105, 104, 103, 104, 101, 100, 105, 123, 117,
172, 172, 172, 168, 170, 177, 170, 175, 151, 149, 150, 150, 147, 147, 156, 161, 161, 161, 151, 126,
93, 90, 102, 107, 104, 103, 104, 101, 104, 104, 122, 117, 172, 172, 170, 168, 170, 177, 172, 175,
150, 149, 152, 151, 148, 151, 160, 159, 157, 157, 148, 133, 96, 90, 103, 107, 104, 104, 101, 100,
102, 102, 121, 117, 170, 170, 169, 171, 171, 179, 173, 175, 149, 151, 152, 151, 148, 154, 162, 157,
154, 154, 151, 132, 92, 89, 101, 108, 104, 102, 101, 101, 103, 103, 123, 118, 171, 168, 177, 173,
171, 178, 172, 176, 152, 152, 152, 151, 154, 162, 161, 155, 149, 157, 156, 129, 92, 87, 101, 107,
102, 100, 107, 100, 101, 102, 123, 118, 170, 175, 182, 172, 171, 179, 173, 175, 152, 151, 154, 155,
160, 162, 161, 153, 150, 156, 153, 129, 92, 91, 102, 106, 100, 109, 115, 99, 101, 102, 124, 120,
171, 179, 178, 172, 171, 181, 171, 173, 154, 154, 154, 162, 160, 158, 156, 152, 153, 157, 151, 128,
86, 86, 102, 105, 102, 122, 114, 99, 101, 102, 125, 120, 178, 173, 177, 172, 171, 180, 172, 173,
154, 152, 158, 163, 150, 148, 148, 156, 151, 158, 152, 129, 87, 87, 101, 105
};
byte[] b =
{
103, 103, 103, 103, 101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175, 150, 150, 150, 150,
146, 149, 152, 154, 161, 164, 151, 130, 93, 86, 100, 106, 103, 103, 103, 103, 101, 106, 122, 114,
171, 171, 171, 171, 171, 177, 169, 175, 150, 150, 150, 150, 146, 149, 152, 154, 158, 161, 148, 127,
93, 86, 100, 106, 103, 103, 103, 103, 101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175,
150, 150, 150, 150, 146, 149, 152, 154, 156, 159, 146, 125, 99, 92, 106, 112, 103, 103, 103, 103,
101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175, 148, 148, 148, 148, 149, 158, 162, 159,
155, 155, 153, 129, 94, 87, 101, 106, 102, 100, 100, 102, 100, 101, 120, 122, 170, 176, 176, 170,
174, 180, 171, 177, 151, 151, 151, 151, 152, 159, 161, 156, 155, 155, 153, 129, 94, 87, 101, 106,
102, 105, 105, 102, 100, 101, 120, 122, 170, 176, 176, 170, 174, 180, 171, 177, 154, 154, 154, 154,
156, 161, 159, 152, 155, 155, 153, 129, 94, 87, 101, 106, 102, 112, 112, 102, 100, 101, 120, 122,
170, 176, 176, 170, 174, 180, 171, 177, 156, 156, 156, 156, 159, 162, 158, 149, 155, 155, 153, 129,
94, 87, 101, 106, 102, 117, 117, 102, 100, 101, 120, 122, 170, 176, 176, 170, 174, 180, 171, 177,
152, 153, 157, 162, 150, 149, 149, 151, 155, 160, 150, 131, 91, 90, 104, 104
};
int expected = 749;
// act
int actual = LossyUtils.Vp8_Sse16X8(a, b);
// assert
Assert.Equal(expected, actual);
}
private static void RunVp8Sse4X4Test()
{
// arrange
@ -168,6 +286,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void RunTransformOne_Works() => RunTransformOneTest();
[Fact]
public void Vp8Sse16X16_Works() => RunVp8Sse16X16Test();
[Fact]
public void Vp8Sse16X8_Works() => RunVp8Sse16X8Test();
[Fact]
public void Vp8Sse4X4_Works() => RunVp8Sse4X4Test();
@ -190,6 +314,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void TransformOne_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformOneTest, HwIntrinsics.DisableHWIntrinsic);
[Fact]
public void Vp8Sse16X16_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.AllowAll);
[Fact]
public void Vp8Sse16X16_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.DisableSSE2);
[Fact]
public void Vp8Sse16X16_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.DisableAVX2);
[Fact]
public void Vp8Sse16X8_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.AllowAll);
[Fact]
public void Vp8Sse16X8_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.DisableSSE2);
[Fact]
public void Vp8Sse16X8_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.DisableAVX2);
[Fact]
public void Vp8Sse4X4_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.AllowAll);

111
tests/ImageSharp.Tests/Formats/WebP/Vp8HistogramTests.cs

@ -3,6 +3,7 @@
using System.Collections.Generic;
using SixLabors.ImageSharp.Formats.Webp.Lossy;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Webp
@ -67,6 +68,108 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
}
}
private static void RunCollectHistogramTest()
{
// arrange
var histogram = new Vp8Histogram();
byte[] reference =
{
154, 154, 151, 151, 149, 148, 151, 157, 163, 163, 154, 132, 102, 98, 104, 108, 107, 104, 104, 103,
101, 106, 123, 119, 170, 171, 172, 171, 168, 175, 171, 173, 151, 151, 149, 150, 147, 147, 146, 159,
164, 165, 154, 129, 92, 90, 101, 105, 104, 103, 104, 101, 100, 105, 123, 117, 172, 172, 172, 168,
170, 177, 170, 175, 151, 149, 150, 150, 147, 147, 156, 161, 161, 161, 151, 126, 93, 90, 102, 107,
104, 103, 104, 101, 104, 104, 122, 117, 172, 172, 170, 168, 170, 177, 172, 175, 150, 149, 152, 151,
148, 151, 160, 159, 157, 157, 148, 133, 96, 90, 103, 107, 104, 104, 101, 100, 102, 102, 121, 117,
170, 170, 169, 171, 171, 179, 173, 175, 149, 151, 152, 151, 148, 154, 162, 157, 154, 154, 151, 132,
92, 89, 101, 108, 104, 102, 101, 101, 103, 103, 123, 118, 171, 168, 177, 173, 171, 178, 172, 176,
152, 152, 152, 151, 154, 162, 161, 155, 149, 157, 156, 129, 92, 87, 101, 107, 102, 100, 107, 100,
101, 102, 123, 118, 170, 175, 182, 172, 171, 179, 173, 175, 152, 151, 154, 155, 160, 162, 161, 153,
150, 156, 153, 129, 92, 91, 102, 106, 100, 109, 115, 99, 101, 102, 124, 120, 171, 179, 178, 172,
171, 181, 171, 173, 154, 154, 154, 162, 160, 158, 156, 152, 153, 157, 151, 128, 86, 86, 102, 105,
102, 122, 114, 99, 101, 102, 125, 120, 178, 173, 177, 172, 171, 180, 172, 173, 154, 152, 158, 163,
150, 148, 148, 156, 151, 158, 152, 129, 87, 87, 101, 105, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 204, 154, 151, 165, 156, 141, 137, 146, 158, 152, 159, 152, 133,
90, 88, 99, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
154, 160, 164, 150, 126, 127, 149, 159, 155, 161, 153, 131, 84, 86, 97, 103, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 157, 167, 157, 137, 102, 128, 155, 161,
157, 159, 154, 134, 84, 82, 97, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 163, 163, 150, 113, 78, 132, 156, 162, 159, 160, 154, 132, 83, 78, 91, 97, 204,
204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 163, 157, 137, 80, 78,
131, 154, 163, 157, 159, 149, 131, 82, 77, 94, 100, 204, 204, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 159, 151, 108, 72, 88, 132, 156, 162, 159, 157, 151, 130, 79, 78,
95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 151, 130,
82, 82, 89, 134, 154, 161, 161, 157, 152, 129, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204,
204, 204, 204, 204, 204, 204, 204, 204, 204
};
byte[] pred =
{
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 127, 127, 127, 127, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
127, 127, 127, 127, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 127, 127, 127, 127, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
127, 127, 127, 127, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 127, 127, 127, 127, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
127, 127, 127, 127, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 129, 129, 129, 129,
129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 127, 127, 127, 127, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
129, 129, 129, 129
};
int expectedAlpha = 146;
// act
histogram.CollectHistogram(reference, pred, 0, 10);
int actualAlpha = histogram.GetAlpha();
// assert
Assert.Equal(expectedAlpha, actualAlpha);
}
[Fact]
public void RunCollectHistogramTest_Works() => RunCollectHistogramTest();
[Fact]
public void GetAlpha_WithEmptyHistogram_Works()
{
@ -111,5 +214,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
// assert
Assert.Equal(1054, alpha);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void CollectHistogramTest_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectHistogramTest, HwIntrinsics.AllowAll);
[Fact]
public void CollectHistogramTest_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectHistogramTest, HwIntrinsics.DisableHWIntrinsic);
#endif
}
}

1
tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs

@ -12,7 +12,6 @@ using static SixLabors.ImageSharp.Tests.TestImages.Webp;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Webp
{
[Collection("RunSerial")]
[Trait("Format", "Webp")]
public class WebpDecoderTests
{

1
tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs

@ -11,7 +11,6 @@ using static SixLabors.ImageSharp.Tests.TestImages.Webp;
namespace SixLabors.ImageSharp.Tests.Formats.Webp
{
[Collection("RunSerial")]
[Trait("Format", "Webp")]
public class WebpEncoderTests
{

14
tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -259,6 +259,12 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators
[InlineData(1200)] // Group of two UniformUnmanagedMemoryPool buffers
public void AllocateMemoryGroup_Finalization_ReturnsToPool(int length)
{
if (TestEnvironment.IsOSX)
{
// Skip on OSX: https://github.com/SixLabors/ImageSharp/issues/1887
return;
}
if (!TestEnvironment.RunsOnCI)
{
// This may fail in local runs resulting in high memory load.
@ -315,6 +321,12 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators
[InlineData(600)] // Group of single UniformUnmanagedMemoryPool buffer
public void AllocateSingleMemoryOwner_Finalization_ReturnsToPool(int length)
{
if (TestEnvironment.IsOSX)
{
// Skip on OSX: https://github.com/SixLabors/ImageSharp/issues/1887
return;
}
if (!TestEnvironment.RunsOnCI)
{
// This may fail in local runs resulting in high memory load.

Loading…
Cancel
Save