Browse Source

Remove SUPPORTS conditions

pull/2189/head
James Jackson-South 4 years ago
parent
commit
eee64f25d6
  1. 2
      shared-infrastructure
  2. 4
      src/Directory.Build.props
  3. 7
      src/ImageSharp.ruleset
  4. 17
      src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
  5. 35
      src/ImageSharp/Common/Extensions/EncoderExtensions.cs
  6. 46
      src/ImageSharp/Common/Extensions/StreamExtensions.cs
  7. 6
      src/ImageSharp/Common/Helpers/InliningOptions.cs
  8. 128
      src/ImageSharp/Common/Helpers/Numerics.cs
  9. 216
      src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
  10. 35
      src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
  11. 20
      src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs
  12. 88
      src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
  13. 26
      src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs
  14. 18
      src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs
  15. 19
      src/ImageSharp/Common/Helpers/SimdUtils.cs
  16. 22
      src/ImageSharp/Compression/Zlib/Adler32.cs
  17. 16
      src/ImageSharp/Compression/Zlib/Crc32.cs
  18. 6
      src/ImageSharp/Compression/Zlib/DeflaterEngine.cs
  19. 2
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs
  20. 11
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
  21. 4
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs
  22. 34
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  23. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmykAvx.cs
  24. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs
  25. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs
  26. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs
  27. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx.cs
  28. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterAvx.cs
  29. 10
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterBase.cs
  30. 15
      src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
  31. 6
      src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs
  32. 10
      src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs
  33. 38
      src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs
  34. 6
      src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs
  35. 6
      src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.cs
  36. 2
      src/ImageSharp/Formats/Jpeg/Components/ZigZag.Intrinsic.cs
  37. 9
      src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
  38. 13
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  39. 11
      src/ImageSharp/Formats/Png/Filters/SubFilter.cs
  40. 9
      src/ImageSharp/Formats/Png/Filters/UpFilter.cs
  41. 24
      src/ImageSharp/Formats/Webp/AlphaDecoder.cs
  42. 54
      src/ImageSharp/Formats/Webp/Lossless/ColorSpaceTransformUtils.cs
  43. 82
      src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs
  44. 4
      src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs
  45. 151
      src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs
  46. 42
      src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs
  47. 124
      src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs
  48. 6
      src/ImageSharp/Formats/Webp/Lossy/Vp8Histogram.cs
  49. 25
      src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs
  50. 80
      src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs
  51. 28
      src/ImageSharp/Formats/Webp/WebpCommonUtils.cs
  52. 4
      src/ImageSharp/IO/BufferedReadStream.cs
  53. 4
      src/ImageSharp/IO/ChunkedMemoryStream.cs
  54. 16
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
  55. 4
      src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgb24.PixelOperations.cs
  56. 8
      src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Rgba32.PixelOperations.cs
  57. 8
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
  58. 10
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs
  59. 4
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs
  60. 2
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs
  61. 2
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs
  62. 2
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs
  63. 2
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs
  64. 2
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs
  65. 13
      tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs
  66. 19
      tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs
  67. 10
      tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs
  68. 4
      tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs
  69. 93
      tests/ImageSharp.Tests/Common/SimdUtilsTests.cs
  70. 4
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  71. 22
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
  72. 14
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Internal.cs
  73. 30
      tests/ImageSharp.Tests/Formats/Jpg/RgbToYCbCrConverterTests.cs
  74. 2
      tests/ImageSharp.Tests/Formats/Png/Adler32Tests.cs
  75. 2
      tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs
  76. 35
      tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs
  77. 3
      tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs
  78. 16
      tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs
  79. 2
      tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs
  80. 8
      tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs
  81. 2
      tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs
  82. 2
      tests/ImageSharp.Tests/Formats/WebP/Vp8EncodingTests.cs
  83. 2
      tests/ImageSharp.Tests/Formats/WebP/Vp8HistogramTests.cs
  84. 2
      tests/ImageSharp.Tests/Formats/WebP/Vp8LHistogramTests.cs
  85. 2
      tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs
  86. 8
      tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs
  87. 240
      tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
  88. 2
      tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
  89. 19
      tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs
  90. 2
      tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs
  91. 17
      tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs

2
shared-infrastructure

@ -1 +1 @@
Subproject commit c0e0353c1ee89398def0ccdc3e945380034fbea8
Subproject commit e12c30b3b1427815cffa22ac95c77ba29f65fe5a

4
src/Directory.Build.props

@ -21,6 +21,10 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\ImageSharp.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<!-- DynamicProxyGenAssembly2 is needed so Moq can use our internals -->
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" Key="0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" />

7
src/ImageSharp.ruleset

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="ImageSharp" ToolsVersion="17.0">
<Include Path="..\shared-infrastructure\sixlabors.ruleset" Action="Default" />
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="SA1011" Action="None" />
</Rules>
</RuleSet>

17
src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs

@ -5,10 +5,8 @@ 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;
#endif
namespace SixLabors.ImageSharp.ColorSpaces.Companding
{
@ -25,10 +23,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
private const int Length = Scale + 2; // 256kb @ 16bit precision.
private const int Scale = (1 << 16) - 1;
private static readonly Lazy<float[]> LazyCompressTable = new Lazy<float[]>(
private static readonly Lazy<float[]> LazyCompressTable = new(
() =>
{
var result = new float[Length];
float[] result = new float[Length];
for (int i = 0; i < result.Length; i++)
{
@ -49,10 +47,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
},
true);
private static readonly Lazy<float[]> LazyExpandTable = new Lazy<float[]>(
private static readonly Lazy<float[]> LazyExpandTable = new(
() =>
{
var result = new float[Length];
float[] result = new float[Length];
for (int i = 0; i < result.Length; i++)
{
@ -84,7 +82,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Expand(Span<Vector4> vectors)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && vectors.Length >= 2)
{
CompandAvx2(vectors, ExpandTable);
@ -96,7 +93,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
}
}
else
#endif
{
CompandScalar(vectors, ExpandTable);
}
@ -109,7 +105,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Compress(Span<Vector4> vectors)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && vectors.Length >= 2)
{
CompandAvx2(vectors, CompressTable);
@ -121,7 +116,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
}
}
else
#endif
{
CompandScalar(vectors, CompressTable);
}
@ -171,8 +165,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
public static float Compress(float channel)
=> channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F;
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe void CompandAvx2(Span<Vector4> vectors, float[] table)
{
@ -204,7 +196,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
}
}
}
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe void CompandScalar(Span<Vector4> vectors, float[] table)

35
src/ImageSharp/Common/Extensions/EncoderExtensions.cs

@ -1,35 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if !SUPPORTS_ENCODING_STRING
using System;
using System.Text;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for the <see cref="Encoder"/> type.
/// </summary>
internal static unsafe class EncoderExtensions
{
/// <summary>
/// Gets a string from the provided buffer data.
/// </summary>
/// <param name="encoding">The encoding.</param>
/// <param name="buffer">The buffer.</param>
/// <returns>The string.</returns>
public static string GetString(this Encoding encoding, ReadOnlySpan<byte> buffer)
{
if (buffer.Length == 0)
{
return string.Empty;
}
fixed (byte* bytes = buffer)
{
return encoding.GetString(bytes, buffer.Length);
}
}
}
}
#endif

46
src/ImageSharp/Common/Extensions/StreamExtensions.cs

@ -70,51 +70,5 @@ namespace SixLabors.ImageSharp
ArrayPool<byte>.Shared.Return(buffer);
}
}
#if !SUPPORTS_SPAN_STREAM
// This is a port of the CoreFX implementation and is MIT Licensed:
// https://github.com/dotnet/corefx/blob/17300169760c61a90cab8d913636c1058a30a8c1/src/Common/src/CoreLib/System/IO/Stream.cs#L742
public static int Read(this Stream stream, Span<byte> buffer)
{
// This uses ArrayPool<byte>.Shared, rather than taking a MemoryAllocator,
// in order to match the signature of the framework method that exists in
// .NET Core.
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
int numRead = stream.Read(sharedBuffer, 0, buffer.Length);
if ((uint)numRead > (uint)buffer.Length)
{
throw new IOException("Stream was too long.");
}
new Span<byte>(sharedBuffer, 0, numRead).CopyTo(buffer);
return numRead;
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}
// This is a port of the CoreFX implementation and is MIT Licensed:
// https://github.com/dotnet/corefx/blob/17300169760c61a90cab8d913636c1058a30a8c1/src/Common/src/CoreLib/System/IO/Stream.cs#L775
public static void Write(this Stream stream, ReadOnlySpan<byte> buffer)
{
// This uses ArrayPool<byte>.Shared, rather than taking a MemoryAllocator,
// in order to match the signature of the framework method that exists in
// .NET Core.
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
buffer.CopyTo(sharedBuffer);
stream.Write(sharedBuffer, 0, buffer.Length);
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}
#endif
}
}

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

@ -18,13 +18,11 @@ namespace SixLabors.ImageSharp
public const MethodImplOptions AlwaysInline = MethodImplOptions.AggressiveInlining;
#if PROFILING
public const MethodImplOptions HotPath = MethodImplOptions.NoInlining;
public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining;
#else
#if SUPPORTS_HOTPATH
public const MethodImplOptions HotPath = MethodImplOptions.AggressiveOptimization;
#else
public const MethodImplOptions HotPath = MethodImplOptions.AggressiveInlining;
#endif
public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining;
#endif
public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining;

128
src/ImageSharp/Common/Helpers/Numerics.cs

@ -5,10 +5,8 @@ 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;
#endif
namespace SixLabors.ImageSharp
{
@ -18,20 +16,8 @@ namespace SixLabors.ImageSharp
/// </summary>
internal static class Numerics
{
#if SUPPORTS_RUNTIME_INTRINSICS
public const int BlendAlphaControl = 0b_10_00_10_00;
private const int ShuffleAlphaControl = 0b_11_11_11_11;
#endif
#if !SUPPORTS_BITOPERATIONS
private static ReadOnlySpan<byte> Log2DeBruijn => new byte[32]
{
00, 09, 01, 10, 13, 21, 02, 29,
11, 14, 16, 18, 22, 25, 03, 30,
08, 12, 20, 28, 15, 17, 24, 07,
19, 27, 23, 06, 26, 05, 04, 31
};
#endif
/// <summary>
/// Determine the Greatest CommonDivisor (GCD) of two numbers.
@ -129,13 +115,13 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Gaussian(float x, float sigma)
{
const float Numerator = 1.0f;
const float numerator = 1.0f;
float denominator = MathF.Sqrt(2 * MathF.PI) * sigma;
float exponentNumerator = -x * x;
float exponentDenominator = 2 * Pow2(sigma);
float left = Numerator / denominator;
float left = numerator / denominator;
float right = MathF.Exp(exponentNumerator / exponentDenominator);
return left * right;
@ -300,7 +286,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clamp(Span<byte> span, byte min, byte max)
{
Span<byte> remainder = span.Slice(ClampReduce(span, min, max));
Span<byte> remainder = span[ClampReduce(span, min, max)..];
if (remainder.Length > 0)
{
@ -325,7 +311,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clamp(Span<uint> span, uint min, uint max)
{
Span<uint> remainder = span.Slice(ClampReduce(span, min, max));
Span<uint> remainder = span[ClampReduce(span, min, max)..];
if (remainder.Length > 0)
{
@ -350,7 +336,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clamp(Span<int> span, int min, int max)
{
Span<int> remainder = span.Slice(ClampReduce(span, min, max));
Span<int> remainder = span[ClampReduce(span, min, max)..];
if (remainder.Length > 0)
{
@ -375,7 +361,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clamp(Span<float> span, float min, float max)
{
Span<float> remainder = span.Slice(ClampReduce(span, min, max));
Span<float> remainder = span[ClampReduce(span, min, max)..];
if (remainder.Length > 0)
{
@ -400,7 +386,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clamp(Span<double> span, double min, double max)
{
Span<double> remainder = span.Slice(ClampReduce(span, min, max));
Span<double> remainder = span[ClampReduce(span, min, max)..];
if (remainder.Length > 0)
{
@ -427,7 +413,7 @@ namespace SixLabors.ImageSharp
if (adjustedCount > 0)
{
ClampImpl(span.Slice(0, adjustedCount), min, max);
ClampImpl(span[..adjustedCount], min, max);
}
return adjustedCount;
@ -512,7 +498,6 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Premultiply(Span<Vector4> vectors)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && vectors.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
@ -530,11 +515,10 @@ namespace SixLabors.ImageSharp
if (Modulo2(vectors.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
Premultiply(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1)));
Premultiply(ref MemoryMarshal.GetReference(vectors[^1..]));
}
}
else
#endif
{
ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors);
ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, vectors.Length);
@ -555,7 +539,6 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnPremultiply(Span<Vector4> vectors)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && vectors.Length >= 2)
{
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
@ -573,11 +556,10 @@ namespace SixLabors.ImageSharp
if (Modulo2(vectors.Length) != 0)
{
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
UnPremultiply(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1)));
UnPremultiply(ref MemoryMarshal.GetReference(vectors[^1..]));
}
}
else
#endif
{
ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors);
ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, vectors.Length);
@ -627,7 +609,6 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void CubeRootOnXYZ(Span<Vector4> vectors)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse41.IsSupported)
{
ref Vector128<float> vectors128Ref = ref Unsafe.As<Vector4, Vector128<float>>(ref MemoryMarshal.GetReference(vectors));
@ -678,7 +659,6 @@ namespace SixLabors.ImageSharp
}
}
else
#endif
{
ref Vector4 vectorsRef = ref MemoryMarshal.GetReference(vectors);
ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsRef, vectors.Length);
@ -727,8 +707,6 @@ namespace SixLabors.ImageSharp
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
/// <summary>
/// Performs a linear interpolation between two values based on the given weighting.
/// </summary>
@ -752,7 +730,6 @@ namespace SixLabors.ImageSharp
return Avx.Add(Avx.Multiply(diff, amount), value1);
}
}
#endif
/// <summary>
/// Performs a linear interpolation between two values based on the given weighting.
@ -765,8 +742,6 @@ namespace SixLabors.ImageSharp
public static float Lerp(float value1, float value2, float amount)
=> ((value2 - value1) * amount) + value1;
#if SUPPORTS_RUNTIME_INTRINSICS
/// <summary>
/// Accumulates 8-bit integers into <paramref name="accumulator"/> by
/// widening them to 32-bit integers and performing four additions.
@ -860,51 +835,14 @@ namespace SixLabors.ImageSharp
// Vector128<int>.ToScalar() isn't optimized pre-net5.0 https://github.com/dotnet/runtime/pull/37882
return Sse2.ConvertToInt32(vsum);
}
#endif
/// <summary>
/// Calculates floored log of the specified value, base 2.
/// Note that by convention, input value 0 returns 0 since Log(0) is undefined.
/// </summary>
/// <param name="value">The value.</param>
public static int Log2(uint value)
{
#if SUPPORTS_BITOPERATIONS
return BitOperations.Log2(value);
#else
return Log2SoftwareFallback(value);
#endif
}
#if !SUPPORTS_BITOPERATIONS
/// <summary>
/// Calculates floored log of the specified value, base 2.
/// Note that by convention, input value 0 returns 0 since Log(0) is undefined.
/// Bit hacking with deBruijn sequence, extremely fast yet does not use any intrinsics so will work on every platform/runtime.
/// </summary>
/// <remarks>
/// Description of this bit hacking can be found here:
/// https://cstheory.stackexchange.com/questions/19524/using-the-de-bruijn-sequence-to-find-the-lceil-log-2-v-rceil-of-an-integer
/// </remarks>
/// <param name="value">The value.</param>
private static int Log2SoftwareFallback(uint value)
{
// No AggressiveInlining due to large method size
// Has conventional contract 0->0 (Log(0) is undefined) by default, no need for if checking
// Fill trailing zeros with ones, eg 00010010 becomes 00011111
value |= value >> 01;
value |= value >> 02;
value |= value >> 04;
value |= value >> 08;
value |= value >> 16;
// uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check
return Unsafe.AddByteOffset(
ref MemoryMarshal.GetReference(Log2DeBruijn),
(IntPtr)(int)((value * 0x07C4ACDDu) >> 27)); // uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here
}
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Log2(uint value) => BitOperations.Log2(value);
/// <summary>
/// Fast division with ceiling for <see cref="uint"/> numbers.
@ -921,44 +859,8 @@ namespace SixLabors.ImageSharp
/// <param name="offset">The number of bits to rotate with.</param>
/// <returns>The rotated value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint RotateLeft(uint value, int offset)
{
#if SUPPORTS_BITOPERATIONS
return BitOperations.RotateLeft(value, offset);
#else
return RotateLeftSoftwareFallback(value, offset);
#endif
}
#if !SUPPORTS_BITOPERATIONS
/// <summary>
/// Rotates the specified value left by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="offset">The number of bits to rotate with.</param>
/// <returns>The rotated value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint RotateLeftSoftwareFallback(uint value, int offset)
=> (value << offset) | (value >> (32 - offset));
#endif
/// <summary>
/// Rotates the specified value right by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="offset">The number of bits to rotate with.</param>
/// <returns>The rotated value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint RotateRight(uint value, int offset)
{
#if SUPPORTS_BITOPERATIONS
return BitOperations.RotateRight(value, offset);
#else
return RotateRightSoftwareFallback(value, offset);
#endif
}
public static uint RotateLeft(uint value, int offset) => BitOperations.RotateLeft(value, offset);
#if !SUPPORTS_BITOPERATIONS
/// <summary>
/// Rotates the specified value right by the specified number of bits.
/// </summary>
@ -966,9 +868,7 @@ namespace SixLabors.ImageSharp
/// <param name="offset">The number of bits to rotate with.</param>
/// <returns>The rotated value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint RotateRightSoftwareFallback(uint value, int offset)
=> (value >> offset) | (value << (32 - offset));
#endif
public static uint RotateRight(uint value, int offset) => BitOperations.RotateRight(value, offset);
/// <summary>
/// Tells whether input value is outside of the given range.

216
src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs

@ -1,216 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Tuples;
// ReSharper disable MemberHidesStaticFromOuterClass
namespace SixLabors.ImageSharp
{
internal static partial class SimdUtils
{
/// <summary>
/// Implementation with 256bit / AVX2 intrinsics NOT depending on newer API-s (Vector.Widen etc.)
/// </summary>
public static class BasicIntrinsics256
{
public static bool IsAvailable { get; } = HasVector8;
#if !SUPPORTS_EXTENDED_INTRINSICS
/// <summary>
/// <see cref="ByteToNormalizedFloat"/> as many elements as possible, slicing them down (keeping the remainder).
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void ByteToNormalizedFloatReduce(
ref ReadOnlySpan<byte> source,
ref Span<float> dest)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
if (!IsAvailable)
{
return;
}
int remainder = Numerics.Modulo8(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
{
ByteToNormalizedFloat(
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount));
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
}
}
/// <summary>
/// <see cref="NormalizedFloatToByteSaturate"/> as many elements as possible, slicing them down (keeping the remainder).
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void NormalizedFloatToByteSaturateReduce(
ref ReadOnlySpan<float> source,
ref Span<byte> dest)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
if (!IsAvailable)
{
return;
}
int remainder = Numerics.Modulo8(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
{
NormalizedFloatToByteSaturate(source.Slice(0, adjustedCount), dest.Slice(0, adjustedCount));
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
}
}
#endif
/// <summary>
/// SIMD optimized implementation for <see cref="SimdUtils.ByteToNormalizedFloat"/>.
/// Works only with span Length divisible by 8.
/// Implementation adapted from:
/// http://lolengine.net/blog/2011/3/20/understanding-fast-float-integer-conversions
/// http://stackoverflow.com/a/536278
/// </summary>
internal static void ByteToNormalizedFloat(ReadOnlySpan<byte> source, Span<float> dest)
{
VerifyHasVector8(nameof(ByteToNormalizedFloat));
VerifySpanInput(source, dest, 8);
var bVec = new Vector<float>(256.0f / 255.0f);
var magicFloat = new Vector<float>(32768.0f);
var magicInt = new Vector<uint>(1191182336); // reinterpreted value of 32768.0f
var mask = new Vector<uint>(255);
ref Octet<byte> sourceBase = ref Unsafe.As<byte, Octet<byte>>(ref MemoryMarshal.GetReference(source));
ref Octet<uint> destBaseAsWideOctet = ref Unsafe.As<float, Octet<uint>>(ref MemoryMarshal.GetReference(dest));
ref Vector<float> destBaseAsFloat = ref Unsafe.As<Octet<uint>, Vector<float>>(ref destBaseAsWideOctet);
int n = dest.Length / 8;
for (int i = 0; i < n; i++)
{
ref Octet<byte> s = ref Unsafe.Add(ref sourceBase, i);
ref Octet<uint> d = ref Unsafe.Add(ref destBaseAsWideOctet, i);
d.LoadFrom(ref s);
}
for (int i = 0; i < n; i++)
{
ref Vector<float> df = ref Unsafe.Add(ref destBaseAsFloat, i);
var vi = Vector.AsVectorUInt32(df);
vi &= mask;
vi |= magicInt;
var vf = Vector.AsVectorSingle(vi);
vf = (vf - magicFloat) * bVec;
df = vf;
}
}
/// <summary>
/// Implementation of <see cref="SimdUtils.NormalizedFloatToByteSaturate"/> which is faster on older runtimes.
/// </summary>
internal static void NormalizedFloatToByteSaturate(ReadOnlySpan<float> source, Span<byte> dest)
{
VerifyHasVector8(nameof(NormalizedFloatToByteSaturate));
VerifySpanInput(source, dest, 8);
if (source.Length == 0)
{
return;
}
ref Vector<float> srcBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source));
ref Octet<byte> destBase = ref Unsafe.As<byte, Octet<byte>>(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 8;
var magick = new Vector<float>(32768.0f);
var scale = new Vector<float>(255f) / new Vector<float>(256f);
// need to copy to a temporary struct, because
// SimdUtils.Octet<uint> temp = Unsafe.As<Vector<float>, SimdUtils.Octet<uint>>(ref x)
// does not work. TODO: This might be a CoreClr bug, need to ask/report
var temp = default(Octet<uint>);
ref Vector<float> tempRef = ref Unsafe.As<Octet<uint>, Vector<float>>(ref temp);
for (int i = 0; i < n; i++)
{
// union { float f; uint32_t i; } u;
// u.f = 32768.0f + x * (255.0f / 256.0f);
// return (uint8_t)u.i;
Vector<float> x = Unsafe.Add(ref srcBase, i);
x = Vector.Max(x, Vector<float>.Zero);
x = Vector.Min(x, Vector<float>.One);
x = (x * scale) + magick;
tempRef = x;
ref Octet<byte> d = ref Unsafe.Add(ref destBase, i);
d.LoadFrom(ref temp);
}
}
/// <summary>
/// Convert all <see cref="float"/> values normalized into [0..1] from 'source'
/// into 'dest' buffer of <see cref="byte"/>. The values are scaled up into [0-255] and rounded.
/// This implementation is SIMD optimized and works only when span Length is divisible by 8.
/// Based on:
/// <see>
/// <cref>http://lolengine.net/blog/2011/3/20/understanding-fast-float-integer-conversions</cref>
/// </see>
/// </summary>
internal static void BulkConvertNormalizedFloatToByte(ReadOnlySpan<float> source, Span<byte> dest)
{
VerifyHasVector8(nameof(BulkConvertNormalizedFloatToByte));
VerifySpanInput(source, dest, 8);
if (source.Length == 0)
{
return;
}
ref Vector<float> srcBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source));
ref Octet<byte> destBase = ref Unsafe.As<byte, Octet<byte>>(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 8;
var magick = new Vector<float>(32768.0f);
var scale = new Vector<float>(255f) / new Vector<float>(256f);
// need to copy to a temporary struct, because
// SimdUtils.Octet<uint> temp = Unsafe.As<Vector<float>, SimdUtils.Octet<uint>>(ref x)
// does not work. TODO: This might be a CoreClr bug, need to ask/report
var temp = default(Octet<uint>);
ref Vector<float> tempRef = ref Unsafe.As<Octet<uint>, Vector<float>>(ref temp);
for (int i = 0; i < n; i++)
{
// union { float f; uint32_t i; } u;
// u.f = 32768.0f + x * (255.0f / 256.0f);
// return (uint8_t)u.i;
Vector<float> x = Unsafe.Add(ref srcBase, i);
x = (x * scale) + magick;
tempRef = x;
ref Octet<byte> d = ref Unsafe.Add(ref destBase, i);
d.LoadFrom(ref temp);
}
}
}
}
}

35
src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs

@ -21,12 +21,7 @@ namespace SixLabors.ImageSharp
/// </summary>
public static class ExtendedIntrinsics
{
public static bool IsAvailable { get; } =
#if SUPPORTS_EXTENDED_INTRINSICS
Vector.IsHardwareAccelerated;
#else
false;
#endif
public static bool IsAvailable { get; } = Vector.IsHardwareAccelerated;
/// <summary>
/// Widen and convert a vector of <see cref="short"/> values into 2 vectors of <see cref="float"/>-s.
@ -62,10 +57,10 @@ namespace SixLabors.ImageSharp
if (adjustedCount > 0)
{
ByteToNormalizedFloat(source.Slice(0, adjustedCount), dest.Slice(0, adjustedCount));
ByteToNormalizedFloat(source[..adjustedCount], dest[..adjustedCount]);
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
source = source[adjustedCount..];
dest = dest[adjustedCount..];
}
}
@ -89,12 +84,10 @@ namespace SixLabors.ImageSharp
if (adjustedCount > 0)
{
NormalizedFloatToByteSaturate(
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount));
NormalizedFloatToByteSaturate(source[..adjustedCount], dest[..adjustedCount]);
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
source = source[adjustedCount..];
dest = dest[adjustedCount..];
}
}
@ -160,12 +153,10 @@ namespace SixLabors.ImageSharp
Vector<uint> w2 = ConvertToUInt32(f2);
Vector<uint> w3 = ConvertToUInt32(f3);
Vector<ushort> u0 = Vector.Narrow(w0, w1);
Vector<ushort> u1 = Vector.Narrow(w2, w3);
var u0 = Vector.Narrow(w0, w1);
var u1 = Vector.Narrow(w2, w3);
Vector<byte> b = Vector.Narrow(u0, u1);
Unsafe.Add(ref destBase, i) = b;
Unsafe.Add(ref destBase, i) = Vector.Narrow(u0, u1);
}
}
@ -176,15 +167,15 @@ namespace SixLabors.ImageSharp
vf *= maxBytes;
vf += new Vector<float>(0.5f);
vf = Vector.Min(Vector.Max(vf, Vector<float>.Zero), maxBytes);
Vector<int> vi = Vector.ConvertToInt32(vf);
var vi = Vector.ConvertToInt32(vf);
return Vector.AsVectorUInt32(vi);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector<float> ConvertToSingle(Vector<uint> u)
{
Vector<int> vi = Vector.AsVectorInt32(u);
Vector<float> v = Vector.ConvertToSingle(vi);
var vi = Vector.AsVectorInt32(u);
var v = Vector.ConvertToSingle(vi);
v *= new Vector<float>(1f / 255f);
return v;
}

20
src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs

@ -33,12 +33,10 @@ namespace SixLabors.ImageSharp
if (adjustedCount > 0)
{
ByteToNormalizedFloat(
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount));
ByteToNormalizedFloat(source[..adjustedCount], dest[..adjustedCount]);
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
source = source[adjustedCount..];
dest = dest[adjustedCount..];
}
}
@ -58,11 +56,11 @@ namespace SixLabors.ImageSharp
if (adjustedCount > 0)
{
NormalizedFloatToByteSaturate(
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount));
source[..adjustedCount],
dest[..adjustedCount]);
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
source = source[adjustedCount..];
dest = dest[adjustedCount..];
}
}
@ -83,7 +81,7 @@ namespace SixLabors.ImageSharp
ref ByteVector4 sBase = ref Unsafe.As<byte, ByteVector4>(ref MemoryMarshal.GetReference(source));
ref Vector4 dBase = ref Unsafe.As<float, Vector4>(ref MemoryMarshal.GetReference(dest));
const float Scale = 1f / 255f;
const float scale = 1f / 255f;
Vector4 d = default;
for (int i = 0; i < count; i++)
@ -93,7 +91,7 @@ namespace SixLabors.ImageSharp
d.Y = s.Y;
d.Z = s.Z;
d.W = s.W;
d *= Scale;
d *= scale;
Unsafe.Add(ref dBase, i) = d;
}
}

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -63,12 +62,12 @@ namespace SixLabors.ImageSharp
if (adjustedCount > 0)
{
Shuffle4(
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount),
source[..adjustedCount],
dest[..adjustedCount],
control);
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
source = source[adjustedCount..];
dest = dest[adjustedCount..];
}
}
}
@ -97,12 +96,12 @@ namespace SixLabors.ImageSharp
if (adjustedCount > 0)
{
Shuffle4(
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount),
source[..adjustedCount],
dest[..adjustedCount],
control);
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
source = source[adjustedCount..];
dest = dest[adjustedCount..];
}
}
}
@ -129,12 +128,12 @@ namespace SixLabors.ImageSharp
if (adjustedCount > 0)
{
Shuffle3(
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount),
source[..adjustedCount],
dest[..adjustedCount],
control);
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
source = source[adjustedCount..];
dest = dest[adjustedCount..];
}
}
}
@ -162,12 +161,12 @@ namespace SixLabors.ImageSharp
if (sourceCount > 0)
{
Pad3Shuffle4(
source.Slice(0, sourceCount),
dest.Slice(0, destCount),
source[..sourceCount],
dest[..destCount],
control);
source = source.Slice(sourceCount);
dest = dest.Slice(destCount);
source = source[sourceCount..];
dest = dest[destCount..];
}
}
}
@ -195,12 +194,12 @@ namespace SixLabors.ImageSharp
if (sourceCount > 0)
{
Shuffle4Slice3(
source.Slice(0, sourceCount),
dest.Slice(0, destCount),
source[..sourceCount],
dest[..destCount],
control);
source = source.Slice(sourceCount);
dest = dest.Slice(destCount);
source = source[sourceCount..];
dest = dest[destCount..];
}
}
}
@ -603,10 +602,10 @@ namespace SixLabors.ImageSharp
if (adjustedCount > 0)
{
ByteToNormalizedFloat(source.Slice(0, adjustedCount), dest.Slice(0, adjustedCount));
ByteToNormalizedFloat(source[..adjustedCount], dest[..adjustedCount]);
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
source = source[adjustedCount..];
dest = dest[adjustedCount..];
}
}
}
@ -736,11 +735,11 @@ namespace SixLabors.ImageSharp
if (adjustedCount > 0)
{
NormalizedFloatToByteSaturate(
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount));
source[..adjustedCount],
dest[..adjustedCount]);
source = source.Slice(adjustedCount);
dest = dest.Slice(adjustedCount);
source = source[adjustedCount..];
dest = dest[adjustedCount..];
}
}
}
@ -844,13 +843,12 @@ namespace SixLabors.ImageSharp
int count = redChannel.Length / Vector256<byte>.Count;
ref byte control1Bytes = ref MemoryMarshal.GetReference(SimdUtils.HwIntrinsics.PermuteMaskEvenOdd8x32);
ref byte control1Bytes = ref MemoryMarshal.GetReference(PermuteMaskEvenOdd8x32);
Vector256<uint> control1 = Unsafe.As<byte, Vector256<uint>>(ref control1Bytes);
ref byte control2Bytes = ref MemoryMarshal.GetReference(PermuteMaskShiftAlpha8x32);
Vector256<uint> control2 = Unsafe.As<byte, Vector256<uint>>(ref control2Bytes);
Vector256<byte> a = Vector256.Create((byte)255);
var a = Vector256.Create((byte)255);
Vector256<byte> shuffleAlpha = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ShuffleMaskShiftAlpha));
@ -898,10 +896,10 @@ namespace SixLabors.ImageSharp
}
int slice = count * Vector256<byte>.Count;
redChannel = redChannel.Slice(slice);
greenChannel = greenChannel.Slice(slice);
blueChannel = blueChannel.Slice(slice);
destination = destination.Slice(slice);
redChannel = redChannel[slice..];
greenChannel = greenChannel[slice..];
blueChannel = blueChannel[slice..];
destination = destination[slice..];
}
internal static void PackFromRgbPlanesAvx2Reduce(
@ -916,16 +914,9 @@ namespace SixLabors.ImageSharp
ref Vector256<byte> dBase = ref Unsafe.As<Rgba32, Vector256<byte>>(ref MemoryMarshal.GetReference(destination));
int count = redChannel.Length / Vector256<byte>.Count;
ref byte control1Bytes = ref MemoryMarshal.GetReference(SimdUtils.HwIntrinsics.PermuteMaskEvenOdd8x32);
ref byte control1Bytes = ref MemoryMarshal.GetReference(PermuteMaskEvenOdd8x32);
Vector256<uint> control1 = Unsafe.As<byte, Vector256<uint>>(ref control1Bytes);
ref byte control2Bytes = ref MemoryMarshal.GetReference(PermuteMaskShiftAlpha8x32);
Vector256<uint> control2 = Unsafe.As<byte, Vector256<uint>>(ref control2Bytes);
Vector256<byte> a = Vector256.Create((byte)255);
Vector256<byte> shuffleAlpha = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ShuffleMaskShiftAlpha));
var a = Vector256.Create((byte)255);
for (int i = 0; i < count; i++)
{
@ -957,12 +948,11 @@ namespace SixLabors.ImageSharp
}
int slice = count * Vector256<byte>.Count;
redChannel = redChannel.Slice(slice);
greenChannel = greenChannel.Slice(slice);
blueChannel = blueChannel.Slice(slice);
destination = destination.Slice(slice);
redChannel = redChannel[slice..];
greenChannel = greenChannel[slice..];
blueChannel = blueChannel[slice..];
destination = destination[slice..];
}
}
}
}
#endif

26
src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs

@ -4,10 +4,8 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
@ -15,7 +13,6 @@ namespace SixLabors.ImageSharp
{
[MethodImpl(InliningOptions.ShortMethod)]
internal static void PackFromRgbPlanes(
Configuration configuration,
ReadOnlySpan<byte> redChannel,
ReadOnlySpan<byte> greenChannel,
ReadOnlySpan<byte> blueChannel,
@ -25,13 +22,11 @@ namespace SixLabors.ImageSharp
DebugGuard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!");
DebugGuard.IsTrue(destination.Length > redChannel.Length + 2, nameof(destination), "'destination' must contain a padding of 3 elements!");
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination);
}
else
#endif
{
PackFromRgbPlanesScalarBatchedReduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination);
}
@ -41,7 +36,6 @@ namespace SixLabors.ImageSharp
[MethodImpl(InliningOptions.ShortMethod)]
internal static void PackFromRgbPlanes(
Configuration configuration,
ReadOnlySpan<byte> redChannel,
ReadOnlySpan<byte> greenChannel,
ReadOnlySpan<byte> blueChannel,
@ -51,13 +45,11 @@ namespace SixLabors.ImageSharp
DebugGuard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!");
DebugGuard.IsTrue(destination.Length > redChannel.Length, nameof(destination), "'destination' span should not be shorter than the source channels!");
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination);
}
else
#endif
{
PackFromRgbPlanesScalarBatchedReduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination);
}
@ -106,10 +98,10 @@ namespace SixLabors.ImageSharp
}
int finished = count * 4;
redChannel = redChannel.Slice(finished);
greenChannel = greenChannel.Slice(finished);
blueChannel = blueChannel.Slice(finished);
destination = destination.Slice(finished);
redChannel = redChannel[finished..];
greenChannel = greenChannel[finished..];
blueChannel = blueChannel[finished..];
destination = destination[finished..];
}
private static void PackFromRgbPlanesScalarBatchedReduce(
@ -154,10 +146,10 @@ namespace SixLabors.ImageSharp
}
int finished = count * 4;
redChannel = redChannel.Slice(finished);
greenChannel = greenChannel.Slice(finished);
blueChannel = blueChannel.Slice(finished);
destination = destination.Slice(finished);
redChannel = redChannel[finished..];
greenChannel = greenChannel[finished..];
blueChannel = blueChannel[finished..];
destination = destination[finished..];
}
private static void PackFromRgbPlanesRemainder(

18
src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs

@ -25,9 +25,7 @@ namespace SixLabors.ImageSharp
{
VerifyShuffle4SpanInput(source, dest);
#if SUPPORTS_RUNTIME_INTRINSICS
HwIntrinsics.Shuffle4Reduce(ref source, ref dest, control);
#endif
// Deal with the remainder:
if (source.Length > 0)
@ -52,9 +50,7 @@ namespace SixLabors.ImageSharp
{
VerifyShuffle4SpanInput(source, dest);
#if SUPPORTS_RUNTIME_INTRINSICS
HwIntrinsics.Shuffle4Reduce(ref source, ref dest, shuffle.Control);
#endif
// Deal with the remainder:
if (source.Length > 0)
@ -80,9 +76,7 @@ namespace SixLabors.ImageSharp
// Source length should be smaller than dest length, and divisible by 3.
VerifyShuffle3SpanInput(source, dest);
#if SUPPORTS_RUNTIME_INTRINSICS
HwIntrinsics.Shuffle3Reduce(ref source, ref dest, shuffle.Control);
#endif
// Deal with the remainder:
if (source.Length > 0)
@ -107,9 +101,7 @@ namespace SixLabors.ImageSharp
{
VerifyPad3Shuffle4SpanInput(source, dest);
#if SUPPORTS_RUNTIME_INTRINSICS
HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, shuffle.Control);
#endif
// Deal with the remainder:
if (source.Length > 0)
@ -134,9 +126,7 @@ namespace SixLabors.ImageSharp
{
VerifyShuffle4Slice3SpanInput(source, dest);
#if SUPPORTS_RUNTIME_INTRINSICS
HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, shuffle.Control);
#endif
// Deal with the remainder:
if (source.Length > 0)
@ -266,10 +256,10 @@ namespace SixLabors.ImageSharp
out int p1,
out int p0)
{
p3 = control >> 6 & 0x3;
p2 = control >> 4 & 0x3;
p1 = control >> 2 & 0x3;
p0 = control >> 0 & 0x3;
p3 = (control >> 6) & 0x3;
p2 = (control >> 4) & 0x3;
p1 = (control >> 2) & 0x3;
p0 = (control >> 0) & 0x3;
}
}
}

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

@ -6,11 +6,8 @@ using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp
{
@ -56,8 +53,6 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector<float> FastRound(this Vector<float> v)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
ref Vector256<float> v256 = ref Unsafe.As<Vector<float>, Vector256<float>>(ref v);
@ -65,7 +60,6 @@ namespace SixLabors.ImageSharp
return Unsafe.As<Vector256<float>, Vector<float>>(ref vRound);
}
else
#endif
{
var magic0 = new Vector<int>(int.MinValue); // 0x80000000
var sgn0 = Vector.AsVectorSingle(magic0);
@ -87,13 +81,8 @@ namespace SixLabors.ImageSharp
internal static void ByteToNormalizedFloat(ReadOnlySpan<byte> source, Span<float> dest)
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
#if SUPPORTS_RUNTIME_INTRINSICS
HwIntrinsics.ByteToNormalizedFloatReduce(ref source, ref dest);
#elif SUPPORTS_EXTENDED_INTRINSICS
ExtendedIntrinsics.ByteToNormalizedFloatReduce(ref source, ref dest);
#else
BasicIntrinsics256.ByteToNormalizedFloatReduce(ref source, ref dest);
#endif
// Also deals with the remainder from previous conversions:
FallbackIntrinsics128.ByteToNormalizedFloatReduce(ref source, ref dest);
@ -118,13 +107,7 @@ namespace SixLabors.ImageSharp
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
#if SUPPORTS_RUNTIME_INTRINSICS
HwIntrinsics.NormalizedFloatToByteSaturateReduce(ref source, ref dest);
#elif SUPPORTS_EXTENDED_INTRINSICS
ExtendedIntrinsics.NormalizedFloatToByteSaturateReduce(ref source, ref dest);
#else
BasicIntrinsics256.NormalizedFloatToByteSaturateReduce(ref source, ref dest);
#endif
// Also deals with the remainder from previous conversions:
FallbackIntrinsics128.NormalizedFloatToByteSaturateReduce(ref source, ref dest);

22
src/ImageSharp/Compression/Zlib/Adler32.cs

@ -4,10 +4,8 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
#pragma warning disable IDE0007 // Use implicit type
namespace SixLabors.ImageSharp.Compression.Zlib
@ -29,7 +27,6 @@ namespace SixLabors.ImageSharp.Compression.Zlib
// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
private const uint NMAX = 5552;
#if SUPPORTS_RUNTIME_INTRINSICS
private const int MinBufferSize = 64;
private const int BlockSize = 1 << 5;
@ -40,7 +37,6 @@ namespace SixLabors.ImageSharp.Compression.Zlib
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, // tap1
16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 // tap2
};
#endif
/// <summary>
/// Calculates the Adler32 checksum with the bytes taken from the span.
@ -65,7 +61,6 @@ namespace SixLabors.ImageSharp.Compression.Zlib
return adler;
}
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && buffer.Length >= MinBufferSize)
{
return CalculateAvx2(adler, buffer);
@ -77,13 +72,9 @@ namespace SixLabors.ImageSharp.Compression.Zlib
}
return CalculateScalar(adler, buffer);
#else
return CalculateScalar(adler, buffer);
#endif
}
// Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/adler32_simd.c
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static unsafe uint CalculateSse(uint adler, ReadOnlySpan<byte> buffer)
{
@ -149,15 +140,15 @@ namespace SixLabors.ImageSharp.Compression.Zlib
v_s2 = Sse2.Add(v_s2, Sse2.ShiftLeftLogical(v_ps, 5));
// Sum epi32 ints v_s1(s2) and accumulate in s1(s2).
const byte S2301 = 0b1011_0001; // A B C D -> B A D C
const byte S1032 = 0b0100_1110; // A B C D -> C D A B
const byte s2301 = 0b1011_0001; // A B C D -> B A D C
const byte s1032 = 0b0100_1110; // A B C D -> C D A B
v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, S1032));
v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, s1032));
s1 += v_s1.ToScalar();
v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S2301));
v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S1032));
v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, s2301));
v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, s1032));
s2 = v_s2.ToScalar();
@ -290,7 +281,6 @@ namespace SixLabors.ImageSharp.Compression.Zlib
s2 %= BASE;
}
#endif
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static unsafe uint CalculateScalar(uint adler, ReadOnlySpan<byte> buffer)
@ -301,7 +291,7 @@ namespace SixLabors.ImageSharp.Compression.Zlib
fixed (byte* bufferPtr = buffer)
{
var localBufferPtr = bufferPtr;
byte* localBufferPtr = bufferPtr;
uint length = (uint)buffer.Length;
while (length > 0)

16
src/ImageSharp/Compression/Zlib/Crc32.cs

@ -4,10 +4,8 @@
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.Compression.Zlib
{
@ -22,7 +20,6 @@ namespace SixLabors.ImageSharp.Compression.Zlib
/// </summary>
public const uint SeedValue = 0U;
#if SUPPORTS_RUNTIME_INTRINSICS
private const int MinBufferSize = 64;
private const int ChunksizeMask = 15;
@ -35,7 +32,6 @@ namespace SixLabors.ImageSharp.Compression.Zlib
0x0163cd6124, 0x0000000000, // k5, k0
0x01db710641, 0x01f7011641 // polynomial
};
#endif
/// <summary>
/// Calculates the CRC checksum with the bytes taken from the span.
@ -60,21 +56,14 @@ namespace SixLabors.ImageSharp.Compression.Zlib
return crc;
}
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse41.IsSupported && Pclmulqdq.IsSupported && buffer.Length >= MinBufferSize)
{
return ~CalculateSse(~crc, buffer);
}
else
{
return ~CalculateScalar(~crc, buffer);
}
#else
return ~CalculateScalar(~crc, buffer);
#endif
}
#if SUPPORTS_RUNTIME_INTRINSICS
// Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/crc32_simd.c
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static unsafe uint CalculateSse(uint crc, ReadOnlySpan<byte> buffer)
@ -194,11 +183,10 @@ namespace SixLabors.ImageSharp.Compression.Zlib
x1 = Sse2.Xor(x1, x2);
crc = (uint)Sse41.Extract(x1.AsInt32(), 1);
return buffer.Length - chunksize == 0 ? crc : CalculateScalar(crc, buffer.Slice(chunksize));
return buffer.Length - chunksize == 0 ? crc : CalculateScalar(crc, buffer[chunksize..]);
}
}
}
#endif
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static uint CalculateScalar(uint crc, ReadOnlySpan<byte> buffer)

6
src/ImageSharp/Compression/Zlib/DeflaterEngine.cs

@ -276,8 +276,8 @@ namespace SixLabors.ImageSharp.Compression.Zlib
this.lookahead = 0;
this.prevAvailable = false;
this.matchLen = DeflaterConstants.MIN_MATCH - 1;
this.head.Span.Slice(0, DeflaterConstants.HASH_SIZE).Clear();
this.prev.Span.Slice(0, DeflaterConstants.WSIZE).Clear();
this.head.Span[..DeflaterConstants.HASH_SIZE].Clear();
this.prev.Span[..DeflaterConstants.WSIZE].Clear();
}
/// <summary>
@ -286,7 +286,7 @@ namespace SixLabors.ImageSharp.Compression.Zlib
/// <param name="level">The value to set the level to.</param>
public void SetLevel(int level)
{
if ((level < 0) || (level > 9))
if (level is < 0 or > 9)
{
DeflateThrowHelper.ThrowOutOfRange(nameof(level));
}

2
src/ImageSharp/Formats/Jpeg/Components/Block8x8.Intrinsic.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@ -36,4 +35,3 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public Vector256<short> V67;
}
}
#endif

11
src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs

@ -5,10 +5,8 @@ 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;
#endif
using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
@ -173,7 +171,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)]
public nint GetLastNonZeroIndex()
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
const int equalityMask = unchecked((int)0b1111_1111_1111_1111_1111_1111_1111_1111);
@ -207,7 +204,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return -1;
}
else
#endif
{
nint index = Size - 1;
ref short elemRef = ref Unsafe.As<Block8x8, short>(ref this);
@ -271,12 +267,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
// row #6
Swap(ref Unsafe.Add(ref elemRef, 55), ref Unsafe.Add(ref elemRef, 62));
static void Swap(ref short a, ref short b)
{
short tmp = a;
a = b;
b = tmp;
}
static void Swap(ref short a, ref short b) => (b, a) = (a, b);
}
/// <summary>

4
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -43,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
ref Vector256<float> bBase = ref b.V0;
ref Vector256<short> destRef = ref dest.V01;
Vector256<int> multiplyIntoInt16ShuffleMask = Vector256.Create(0, 1, 4, 5, 2, 3, 6, 7);
var multiplyIntoInt16ShuffleMask = Vector256.Create(0, 1, 4, 5, 2, 3, 6, 7);
for (nint i = 0; i < 8; i += 2)
{
@ -145,4 +144,3 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
}
#endif

34
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

@ -5,10 +5,8 @@ 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;
#endif
using System.Text;
// ReSharper disable InconsistentNaming
@ -160,7 +158,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)]
public void MultiplyInPlace(float value)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported)
{
var valueVec = Vector256.Create(value);
@ -174,7 +171,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
this.V7 = Avx.Multiply(this.V7, valueVec);
}
else
#endif
{
var valueVec = new Vector4(value);
this.V0L *= valueVec;
@ -202,7 +198,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)]
public unsafe void MultiplyInPlace(ref Block8x8F other)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported)
{
this.V0 = Avx.Multiply(this.V0, other.V0);
@ -215,7 +210,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
this.V7 = Avx.Multiply(this.V7, other.V7);
}
else
#endif
{
this.V0L *= other.V0L;
this.V0R *= other.V0R;
@ -243,7 +237,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)]
public void AddInPlace(float value)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported)
{
var valueVec = Vector256.Create(value);
@ -257,7 +250,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
this.V7 = Avx.Add(this.V7, valueVec);
}
else
#endif
{
var valueVec = new Vector4(value);
this.V0L += valueVec;
@ -287,7 +279,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <param name="qt">The quantization table.</param>
public static void Quantize(ref Block8x8F block, ref Block8x8 dest, ref Block8x8F qt)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
MultiplyIntoInt16_Avx2(ref block, ref qt, ref dest);
@ -299,7 +290,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
ZigZag.ApplyTransposingZigZagOrderingSsse3(ref dest);
}
else
#endif
{
for (int i = 0; i < Size; i++)
{
@ -366,13 +356,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)]
public void LoadFrom(ref Block8x8 source)
{
#if SUPPORTS_EXTENDED_INTRINSICS
if (SimdUtils.HasVector8)
{
this.LoadFromInt16ExtendedAvx2(ref source);
return;
}
#endif
this.LoadFromInt16Scalar(ref source);
}
@ -414,7 +403,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <param name="value">Value to compare to.</param>
public bool EqualsToScalar(int value)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
const int equalityMask = unchecked((int)0b1111_1111_1111_1111_1111_1111_1111_1111);
@ -433,20 +421,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return true;
}
#endif
{
ref float scalars = ref Unsafe.As<Block8x8F, float>(ref this);
for (int i = 0; i < Size; i++)
ref float scalars = ref Unsafe.As<Block8x8F, float>(ref this);
for (int i = 0; i < Size; i++)
{
if ((int)Unsafe.Add(ref scalars, i) != value)
{
if ((int)Unsafe.Add(ref scalars, i) != value)
{
return false;
}
return false;
}
return true;
}
return true;
}
/// <inheritdoc />
@ -491,13 +477,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)]
public void TransposeInplace()
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported)
{
this.TransposeInplace_Avx();
}
else
#endif
{
this.TransposeInplace_Scalar();
}

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@ -49,4 +48,3 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
}
}
#endif

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScaleAvx.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@ -36,4 +35,3 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
}
}
#endif

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@ -43,4 +42,3 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
}
}
#endif

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrAvx.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@ -71,4 +70,3 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
}
}
#endif

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccKAvx.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@ -79,4 +78,3 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
}
}
#endif

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

@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
@ -32,4 +31,3 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
}
}
#endif

10
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverterBase.cs

@ -117,9 +117,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary>
private static IEnumerable<JpegColorConverterBase> GetYCbCrConverters(int precision)
{
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromYCbCrAvx(precision);
#endif
yield return new FromYCbCrVector(precision);
yield return new FromYCbCrScalar(precision);
}
@ -129,9 +127,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary>
private static IEnumerable<JpegColorConverterBase> GetYccKConverters(int precision)
{
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromYccKAvx(precision);
#endif
yield return new FromYccKVector(precision);
yield return new FromYccKScalar(precision);
}
@ -141,9 +137,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary>
private static IEnumerable<JpegColorConverterBase> GetCmykConverters(int precision)
{
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromCmykAvx(precision);
#endif
yield return new FromCmykVector(precision);
yield return new FromCmykScalar(precision);
}
@ -153,9 +147,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary>
private static IEnumerable<JpegColorConverterBase> GetGrayScaleConverters(int precision)
{
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromGrayscaleAvx(precision);
#endif
yield return new FromGrayScaleVector(precision);
yield return new FromGrayscaleScalar(precision);
}
@ -165,9 +157,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary>
private static IEnumerable<JpegColorConverterBase> GetRgbConverters(int precision)
{
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromRgbAvx(precision);
#endif
yield return new FromRgbVector(precision);
yield return new FromRgbScalar(precision);
}

15
src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs

@ -558,7 +558,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
internal static int GetHuffmanEncodingLength(uint value)
{
DebugGuard.IsTrue(value <= (1 << 16), "Huffman encoder is supposed to encode a value of 16bit size max");
#if SUPPORTS_BITOPERATIONS
// This should have been implemented as (BitOperations.Log2(value) + 1) as in non-intrinsic implementation
// But internal log2 is implemented like this: (31 - (int)Lzcnt.LeadingZeroCount(value))
@ -567,19 +567,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
// Fallback code if Lzcnt is not supported still use if-check
// But most modern CPUs support this instruction so this should not be a problem
return 32 - BitOperations.LeadingZeroCount(value);
#else
// Ideally:
// if 0 - return 0 in this case
// else - return log2(value) + 1
//
// Hack based on input value constraint:
// We know that input values are guaranteed to be maximum 16 bit large for huffman encoding
// We can safely shift input value for one bit -> log2(value << 1)
// Because of the 16 bit value constraint it won't overflow
// With that input value change we no longer need to add 1 before returning
// And this eliminates need to check if input value is zero - it is a standard convention which Log2SoftwareFallback adheres to
return Numerics.Log2(value << 1);
#endif
}
/// <summary>

6
src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs

@ -5,10 +5,8 @@ using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
@ -98,16 +96,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
Debug.Assert(RgbToYCbCrConverterVectorized.IsSupported, "AVX2 is required to run this converter");
#if SUPPORTS_RUNTIME_INTRINSICS
ref Vector128<byte> l8ByteSpan = ref Unsafe.As<L8, Vector128<byte>>(ref l8Start);
ref Vector256<float> destRef = ref yBlock.V0;
const int bytesPerL8Stride = 8;
for (nint i = 0; i < 8; i++)
{
Unsafe.Add(ref destRef, i) = Avx2.ConvertToVector256Single(Avx2.ConvertToVector256Int32(Unsafe.AddByteOffset(ref l8ByteSpan, bytesPerL8Stride * i)));
Unsafe.Add(ref destRef, i) = Avx.ConvertToVector256Single(Avx2.ConvertToVector256Int32(Unsafe.AddByteOffset(ref l8ByteSpan, bytesPerL8Stride * i)));
}
#endif
}
/// <summary>

10
src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs

@ -5,10 +5,8 @@ using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
@ -120,15 +118,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
Debug.Assert(RgbToYCbCrConverterVectorized.IsSupported, "AVX2 is required to run this converter");
#if SUPPORTS_RUNTIME_INTRINSICS
ref Vector256<byte> rgbByteSpan = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(rgbSpan));
ref Vector256<float> redRef = ref rBlock.V0;
ref Vector256<float> greenRef = ref gBlock.V0;
ref Vector256<float> blueRef = ref bBlock.V0;
var zero = Vector256.Create(0).AsByte();
Vector256<byte> zero = Vector256.Create(0).AsByte();
var extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.MoveFirst24BytesToSeparateLanes));
var extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.ExtractRgb));
Vector256<uint> extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.MoveFirst24BytesToSeparateLanes));
Vector256<byte> extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.ExtractRgb));
Vector256<byte> rgb, rg, bx;
const int bytesPerRgbStride = 24;
@ -145,7 +142,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
Unsafe.Add(ref greenRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32());
Unsafe.Add(ref blueRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32());
}
#endif
}
private static void ConvertScalar(Span<Rgb24> rgbSpan, ref Block8x8F redBlock, ref Block8x8F greenBlock, ref Block8x8F blueBlock)

38
src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs

@ -3,29 +3,17 @@
using System;
using System.Diagnostics;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
internal static class RgbToYCbCrConverterVectorized
{
public static bool IsSupported
{
get
{
#if SUPPORTS_RUNTIME_INTRINSICS
return Avx2.IsSupported;
#else
return false;
#endif
}
}
public static bool IsSupported => Avx2.IsSupported;
public static int AvxCompatibilityPadding
{
@ -48,18 +36,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
// 8 byte padding to rgb byte span will solve this problem without extra code in converters
get
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (IsSupported)
{
return 8;
}
#endif
return 0;
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
internal static ReadOnlySpan<byte> MoveFirst24BytesToSeparateLanes => new byte[]
{
0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0,
@ -71,7 +56,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF,
0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF
};
#endif
/// <summary>
/// Converts 8x8 Rgb24 pixel matrix to YCbCr pixel matrices with 4:4:4 subsampling
@ -85,7 +69,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
Debug.Assert(IsSupported, "AVX2 is required to run this converter");
#if SUPPORTS_RUNTIME_INTRINSICS
var f0299 = Vector256.Create(0.299f);
var f0587 = Vector256.Create(0.587f);
var f0114 = Vector256.Create(0.114f);
@ -95,15 +78,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
var fn0418688 = Vector256.Create(-0.418688f);
var fn0081312F = Vector256.Create(-0.081312F);
var f05 = Vector256.Create(0.5f);
var zero = Vector256.Create(0).AsByte();
Vector256<byte> zero = Vector256.Create(0).AsByte();
ref Vector256<byte> rgbByteSpan = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(rgbSpan));
ref Vector256<float> destYRef = ref yBlock.V0;
ref Vector256<float> destCbRef = ref cbBlock.V0;
ref Vector256<float> destCrRef = ref crBlock.V0;
var extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes));
var extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb));
Vector256<uint> extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes));
Vector256<byte> extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb));
Vector256<byte> rgb, rg, bx;
Vector256<float> r, g, b;
@ -130,7 +113,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
// 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b))
Unsafe.Add(ref destCrRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r));
}
#endif
}
/// <summary>
@ -140,7 +122,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{
Debug.Assert(IsSupported, "AVX2 is required to run this converter");
#if SUPPORTS_RUNTIME_INTRINSICS
var f0299 = Vector256.Create(0.299f);
var f0587 = Vector256.Create(0.587f);
var f0114 = Vector256.Create(0.114f);
@ -150,7 +131,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
var fn0418688 = Vector256.Create(-0.418688f);
var fn0081312F = Vector256.Create(-0.081312F);
var f05 = Vector256.Create(0.5f);
var zero = Vector256.Create(0).AsByte();
Vector256<byte> zero = Vector256.Create(0).AsByte();
ref Vector256<byte> rgbByteSpan = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(rgbSpan));
@ -159,8 +140,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
ref Vector256<float> destCbRef = ref Unsafe.Add(ref Unsafe.As<Block8x8F, Vector256<float>>(ref cbBlock), destOffset);
ref Vector256<float> destCrRef = ref Unsafe.Add(ref Unsafe.As<Block8x8F, Vector256<float>>(ref crBlock), destOffset);
var extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes));
var extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb));
Vector256<uint> extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes));
Vector256<byte> extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb));
Vector256<byte> rgb, rg, bx;
Vector256<float> r, g, b;
@ -231,10 +212,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
// 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b))
Unsafe.Add(ref destCrRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r));
}
#endif
}
#if SUPPORTS_RUNTIME_INTRINSICS
/// <summary>
/// Scales 16x2 matrix to 8x1 using 2x2 average
/// </summary>
@ -254,6 +233,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
return Avx2.Permute4x64(avg2x2.AsDouble(), 0b11_01_10_00).AsSingle();
}
#endif
}
}

6
src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
@ -45,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
block.V0 = Avx.Add(tmp10, tmp11);
block.V4 = Avx.Subtract(tmp10, tmp11);
Vector256<float> mm256_F_0_7071 = Vector256.Create(0.707106781f);
var mm256_F_0_7071 = Vector256.Create(0.707106781f);
Vector256<float> z1 = Avx.Multiply(Avx.Add(tmp12, tmp13), mm256_F_0_7071);
block.V2 = Avx.Add(tmp13, z1);
block.V6 = Avx.Subtract(tmp13, z1);
@ -98,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
Vector256<float> tmp10 = Avx.Add(z5, tmp2);
Vector256<float> tmp11 = Avx.Subtract(z5, tmp2);
Vector256<float> mm256_F_1_4142 = Vector256.Create(1.414213562f);
var mm256_F_1_4142 = Vector256.Create(1.414213562f);
Vector256<float> tmp13 = Avx.Add(tmp1, tmp3);
Vector256<float> tmp12 = SimdUtils.HwIntrinsics.MultiplySubstract(tmp13, Avx.Subtract(tmp1, tmp3), mm256_F_1_4142);
@ -142,4 +141,3 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
}
#endif

6
src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.cs

@ -4,9 +4,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86;
#endif
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
@ -112,13 +110,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <param name="block">Input block.</param>
public static void TransformIDCT(ref Block8x8F block)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported)
{
IDCT8x8_Avx(ref block);
}
else
#endif
{
IDCT_Vector4(ref block);
}
@ -134,13 +130,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <param name="block">Input block.</param>
public static void TransformFDCT(ref Block8x8F block)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported)
{
FDCT8x8_Avx(ref block);
}
else
#endif
{
FDCT_Vector4(ref block);
}

2
src/ImageSharp/Formats/Jpeg/Components/ZigZag.Intrinsic.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@ -304,4 +303,3 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
}
#endif

9
src/ImageSharp/Formats/Png/Filters/AverageFilter.cs

@ -4,11 +4,8 @@
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.Png.Filters
{
@ -35,19 +32,16 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// With pixels positioned like this:
// prev: c b
// row: a d
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported && bytesPerPixel is 4)
{
DecodeSse2(scanline, previousScanline);
}
else
#endif
{
DecodeScalar(scanline, previousScanline, bytesPerPixel);
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeSse2(Span<byte> scanline, Span<byte> previousScanline)
{
@ -81,7 +75,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
offset += 4;
}
}
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline, int bytesPerPixel)
@ -139,7 +132,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += Numerics.Abs(unchecked((sbyte)res));
}
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
Vector256<byte> zero = Vector256<byte>.Zero;
@ -210,7 +202,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += Numerics.ReduceSum(sumAccumulator);
}
#endif
for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */)
{

13
src/ImageSharp/Formats/Png/Filters/PaethFilter.cs

@ -5,11 +5,8 @@ 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;
#endif
namespace SixLabors.ImageSharp.Formats.Png.Filters
{
@ -38,20 +35,16 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// row: a d
// The Paeth function predicts d to be whichever of a, b, or c is nearest to
// p = a + b - c.
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse41.IsSupported && bytesPerPixel is 4)
{
DecodeSse41(scanline, previousScanline);
}
else
#endif
{
DecodeScalar(scanline, previousScanline, bytesPerPixel);
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeSse41(Span<byte> scanline, Span<byte> previousScanline)
{
@ -107,8 +100,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
}
}
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline, int bytesPerPixel)
{
@ -168,7 +159,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += Numerics.Abs(unchecked((sbyte)res));
}
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
Vector256<byte> zero = Vector256<byte>.Zero;
@ -213,7 +203,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += (int)sumAccumulator[i];
}
}
#endif
for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */)
{
@ -261,7 +250,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
return upperLeft;
}
#if SUPPORTS_RUNTIME_INTRINSICS
private static Vector256<byte> PaethPredictor(Vector256<byte> left, Vector256<byte> above, Vector256<byte> upleft)
{
Vector256<byte> zero = Vector256<byte>.Zero;
@ -324,6 +312,5 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
left: above,
right: upperLeft));
}
#endif
}
}

11
src/ImageSharp/Formats/Png/Filters/SubFilter.cs

@ -5,11 +5,8 @@ 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;
#endif
namespace SixLabors.ImageSharp.Formats.Png.Filters
{
@ -29,19 +26,16 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
public static void Decode(Span<byte> scanline, int bytesPerPixel)
{
// The Sub filter predicts each pixel as the previous pixel.
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported && bytesPerPixel is 4)
{
DecodeSse2(scanline);
}
else
#endif
{
DecodeScalar(scanline, bytesPerPixel);
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
private static void DecodeSse2(Span<byte> scanline)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
@ -64,7 +58,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
offset += 4;
}
}
#endif
private static void DecodeScalar(Span<byte> scanline, int bytesPerPixel)
{
@ -110,7 +103,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += Numerics.Abs(unchecked((sbyte)res));
}
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
Vector256<byte> zero = Vector256<byte>.Zero;
@ -151,7 +143,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += (int)sumAccumulator[i];
}
}
#endif
for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */)
{
@ -163,7 +154,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += Numerics.Abs(unchecked((sbyte)res));
}
sum -= 1;
sum--;
}
}
}

9
src/ImageSharp/Formats/Png/Filters/UpFilter.cs

@ -5,11 +5,8 @@ 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;
#endif
namespace SixLabors.ImageSharp.Formats.Png.Filters
{
@ -30,7 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
{
DebugGuard.MustBeSameSized<byte>(scanline, previousScanline, nameof(scanline));
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
DecodeAvx2(scanline, previousScanline);
@ -40,13 +36,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
DecodeSse2(scanline, previousScanline);
}
else
#endif
{
DecodeScalar(scanline, previousScanline);
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
private static void DecodeAvx2(Span<byte> scanline, Span<byte> previousScanline)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
@ -110,7 +104,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
offset++;
}
}
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline)
@ -150,7 +143,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
int x = 0;
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
Vector256<byte> zero = Vector256<byte>.Zero;
@ -191,7 +183,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += (int)sumAccumulator[i];
}
}
#endif
for (; x < scanline.Length; /* Note: ++x happens in the body to avoid one add operation */)
{

24
src/ImageSharp/Formats/Webp/AlphaDecoder.cs

@ -6,13 +6,11 @@ using System.Buffers;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using SixLabors.ImageSharp.Formats.Webp.BitReader;
using SixLabors.ImageSharp.Formats.Webp.Lossless;
using SixLabors.ImageSharp.Memory;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Formats.Webp
{
@ -143,7 +141,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
Span<byte> alphaSpan = this.Alpha.Memory.Span;
if (this.AlphaFilterType == WebpAlphaFilterType.None)
{
dataSpan.Slice(0, pixelCount).CopyTo(alphaSpan);
dataSpan[..pixelCount].CopyTo(alphaSpan);
return;
}
@ -166,8 +164,8 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
prev = dst;
deltas = deltas.Slice(this.Width);
dst = dst.Slice(this.Width);
deltas = deltas[this.Width..];
dst = dst[this.Width..];
}
}
else
@ -199,7 +197,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
Span<byte> alphaSpan = this.Alpha.Memory.Span;
Span<byte> prev = this.PrevRow == 0 ? null : alphaSpan.Slice(this.Width * this.PrevRow);
Span<byte> prev = this.PrevRow == 0 ? null : alphaSpan[(this.Width * this.PrevRow)..];
for (int y = firstRow; y < lastRow; y++)
{
switch (this.AlphaFilterType)
@ -216,7 +214,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
prev = dst;
dst = dst.Slice(stride);
dst = dst[stride..];
}
this.PrevRow = lastRow - 1;
@ -234,8 +232,8 @@ namespace SixLabors.ImageSharp.Formats.Webp
Span<byte> output = this.Alpha.Memory.Span;
Span<uint> pixelData = this.Vp8LDec.Pixels.Memory.Span;
Span<byte> pixelDataAsBytes = MemoryMarshal.Cast<uint, byte>(pixelData);
Span<byte> dst = output.Slice(this.Width * firstRow);
Span<byte> input = pixelDataAsBytes.Slice(this.Vp8LDec.Width * firstRow);
Span<byte> dst = output[(this.Width * firstRow)..];
Span<byte> input = pixelDataAsBytes[(this.Vp8LDec.Width * firstRow)..];
if (this.Vp8LDec.Transforms.Count == 0 || this.Vp8LDec.Transforms[0].TransformType != Vp8LTransformType.ColorIndexingTransform)
{
@ -311,7 +309,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
private static void HorizontalUnfilter(Span<byte> prev, Span<byte> input, Span<byte> dst, int width)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
dst[0] = (byte)(input[0] + (prev.IsEmpty ? 0 : prev[0]));
@ -345,7 +342,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
}
else
#endif
{
byte pred = (byte)(prev.IsEmpty ? 0 : prev[0]);
@ -366,7 +362,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
else
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
nint i;
@ -386,7 +381,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
}
else
#endif
{
for (int i = 0; i < width; i++)
{

54
src/ImageSharp/Formats/Webp/Lossless/ColorSpaceTransformUtils.cs

@ -4,10 +4,8 @@
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.Lossless
{
@ -15,21 +13,20 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
public static void CollectColorBlueTransforms(Span<uint> bgra, int stride, int tileWidth, int tileHeight, int greenToBlue, int redToBlue, Span<int> histo)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && tileWidth >= 16)
{
const int span = 16;
Span<ushort> values = stackalloc ushort[span];
Vector256<byte> collectColorBlueTransformsShuffleLowMask256 = Vector256.Create(255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255, 255, 18, 255, 22, 255, 26, 255, 30, 255, 255, 255, 255, 255, 255, 255, 255);
Vector256<byte> collectColorBlueTransformsShuffleHighMask256 = Vector256.Create(255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255, 255, 18, 255, 22, 255, 26, 255, 30);
Vector256<byte> collectColorBlueTransformsGreenBlueMask256 = Vector256.Create(255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0);
Vector256<byte> collectColorBlueTransformsGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
Vector256<byte> collectColorBlueTransformsBlueMask256 = Vector256.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0);
Vector256<short> multsr = Vector256.Create(LosslessUtils.Cst5b(redToBlue));
Vector256<short> multsg = Vector256.Create(LosslessUtils.Cst5b(greenToBlue));
var collectColorBlueTransformsShuffleLowMask256 = Vector256.Create(255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255, 255, 18, 255, 22, 255, 26, 255, 30, 255, 255, 255, 255, 255, 255, 255, 255);
var collectColorBlueTransformsShuffleHighMask256 = Vector256.Create(255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255, 255, 18, 255, 22, 255, 26, 255, 30);
var collectColorBlueTransformsGreenBlueMask256 = Vector256.Create(255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0);
var collectColorBlueTransformsGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
var collectColorBlueTransformsBlueMask256 = Vector256.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0);
var multsr = Vector256.Create(LosslessUtils.Cst5b(redToBlue));
var multsg = Vector256.Create(LosslessUtils.Cst5b(greenToBlue));
for (int y = 0; y < tileHeight; y++)
{
Span<uint> srcSpan = bgra.Slice(y * stride);
Span<uint> srcSpan = bgra[(y * stride)..];
ref uint inputRef = ref MemoryMarshal.GetReference(srcSpan);
for (nint x = 0; x <= tileWidth - span; x += span)
{
@ -63,23 +60,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int leftOver = tileWidth & (span - 1);
if (leftOver > 0)
{
CollectColorBlueTransformsNoneVectorized(bgra.Slice(tileWidth - leftOver), stride, leftOver, tileHeight, greenToBlue, redToBlue, histo);
CollectColorBlueTransformsNoneVectorized(bgra[(tileWidth - leftOver)..], stride, leftOver, tileHeight, greenToBlue, redToBlue, histo);
}
}
else if (Sse41.IsSupported)
{
const int span = 8;
Span<ushort> values = stackalloc ushort[span];
Vector128<byte> collectColorBlueTransformsShuffleLowMask = Vector128.Create(255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255);
Vector128<byte> collectColorBlueTransformsShuffleHighMask = Vector128.Create(255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 255, 6, 255, 10, 255, 14);
Vector128<byte> collectColorBlueTransformsGreenBlueMask = Vector128.Create(255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0);
Vector128<byte> collectColorBlueTransformsGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
Vector128<byte> collectColorBlueTransformsBlueMask = Vector128.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0);
Vector128<short> multsr = Vector128.Create(LosslessUtils.Cst5b(redToBlue));
Vector128<short> multsg = Vector128.Create(LosslessUtils.Cst5b(greenToBlue));
var collectColorBlueTransformsShuffleLowMask = Vector128.Create(255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255);
var collectColorBlueTransformsShuffleHighMask = Vector128.Create(255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 255, 6, 255, 10, 255, 14);
var collectColorBlueTransformsGreenBlueMask = Vector128.Create(255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0);
var collectColorBlueTransformsGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
var collectColorBlueTransformsBlueMask = Vector128.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0);
var multsr = Vector128.Create(LosslessUtils.Cst5b(redToBlue));
var multsg = Vector128.Create(LosslessUtils.Cst5b(greenToBlue));
for (int y = 0; y < tileHeight; y++)
{
Span<uint> srcSpan = bgra.Slice(y * stride);
Span<uint> srcSpan = bgra[(y * stride)..];
ref uint inputRef = ref MemoryMarshal.GetReference(srcSpan);
for (nint x = 0; x <= tileWidth - span; x += span)
{
@ -113,11 +110,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int leftOver = tileWidth & (span - 1);
if (leftOver > 0)
{
CollectColorBlueTransformsNoneVectorized(bgra.Slice(tileWidth - leftOver), stride, leftOver, tileHeight, greenToBlue, redToBlue, histo);
CollectColorBlueTransformsNoneVectorized(bgra[(tileWidth - leftOver)..], stride, leftOver, tileHeight, greenToBlue, redToBlue, histo);
}
}
else
#endif
{
CollectColorBlueTransformsNoneVectorized(bgra, stride, tileWidth, tileHeight, greenToBlue, redToBlue, histo);
}
@ -140,17 +136,16 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
public static void CollectColorRedTransforms(Span<uint> bgra, int stride, int tileWidth, int tileHeight, int greenToRed, Span<int> histo)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && tileWidth >= 16)
{
Vector256<byte> collectColorRedTransformsGreenMask256 = Vector256.Create(0x00ff00).AsByte();
Vector256<byte> collectColorRedTransformsAndMask256 = Vector256.Create((short)0xff).AsByte();
Vector256<short> multsg = Vector256.Create(LosslessUtils.Cst5b(greenToRed));
var multsg = Vector256.Create(LosslessUtils.Cst5b(greenToRed));
const int span = 16;
Span<ushort> values = stackalloc ushort[span];
for (int y = 0; y < tileHeight; y++)
{
Span<uint> srcSpan = bgra.Slice(y * stride);
Span<uint> srcSpan = bgra[(y * stride)..];
ref uint inputRef = ref MemoryMarshal.GetReference(srcSpan);
for (nint x = 0; x <= tileWidth - span; x += span)
{
@ -181,19 +176,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int leftOver = tileWidth & (span - 1);
if (leftOver > 0)
{
CollectColorRedTransformsNoneVectorized(bgra.Slice(tileWidth - leftOver), stride, leftOver, tileHeight, greenToRed, histo);
CollectColorRedTransformsNoneVectorized(bgra[(tileWidth - leftOver)..], stride, leftOver, tileHeight, greenToRed, histo);
}
}
else if (Sse41.IsSupported)
{
Vector128<byte> collectColorRedTransformsGreenMask = Vector128.Create(0x00ff00).AsByte();
Vector128<byte> collectColorRedTransformsAndMask = Vector128.Create((short)0xff).AsByte();
Vector128<short> multsg = Vector128.Create(LosslessUtils.Cst5b(greenToRed));
var multsg = Vector128.Create(LosslessUtils.Cst5b(greenToRed));
const int span = 8;
Span<ushort> values = stackalloc ushort[span];
for (int y = 0; y < tileHeight; y++)
{
Span<uint> srcSpan = bgra.Slice(y * stride);
Span<uint> srcSpan = bgra[(y * stride)..];
ref uint inputRef = ref MemoryMarshal.GetReference(srcSpan);
for (nint x = 0; x <= tileWidth - span; x += span)
{
@ -224,11 +219,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int leftOver = tileWidth & (span - 1);
if (leftOver > 0)
{
CollectColorRedTransformsNoneVectorized(bgra.Slice(tileWidth - leftOver), stride, leftOver, tileHeight, greenToRed, histo);
CollectColorRedTransformsNoneVectorized(bgra[(tileWidth - leftOver)..], stride, leftOver, tileHeight, greenToRed, histo);
}
}
else
#endif
{
CollectColorRedTransformsNoneVectorized(bgra, stride, tileWidth, tileHeight, greenToRed, histo);
}

82
src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs

@ -4,11 +4,9 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
@ -68,9 +66,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
if (distance < PrefixLookupIdxMax)
{
(int Code, int ExtraBits) prefixCode = WebpLookupTables.PrefixEncodeCode[distance];
extraBits = prefixCode.ExtraBits;
return prefixCode.Code;
(int code, int bits) = WebpLookupTables.PrefixEncodeCode[distance];
extraBits = bits;
return code;
}
return PrefixEncodeBitsNoLut(distance, ref extraBits);
@ -80,11 +78,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
if (distance < PrefixLookupIdxMax)
{
(int Code, int ExtraBits) prefixCode = WebpLookupTables.PrefixEncodeCode[distance];
extraBits = prefixCode.ExtraBits;
(int code, int bits) = WebpLookupTables.PrefixEncodeCode[distance];
extraBits = bits;
extraBitsValue = WebpLookupTables.PrefixEncodeExtraBitsValue[distance];
return prefixCode.Code;
return code;
}
return PrefixEncodeNoLut(distance, ref extraBits, ref extraBitsValue);
@ -96,10 +94,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <param name="pixelData">The pixel data to apply the transformation.</param>
public static void AddGreenToBlueAndRed(Span<uint> pixelData)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
Vector256<byte> addGreenToBlueAndRedMaskAvx2 = Vector256.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255, 17, 255, 17, 255, 21, 255, 21, 255, 25, 255, 25, 255, 29, 255, 29, 255);
var addGreenToBlueAndRedMaskAvx2 = Vector256.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255, 17, 255, 17, 255, 21, 255, 21, 255, 25, 255, 25, 255, 29, 255, 29, 255);
int numPixels = pixelData.Length;
nint i;
for (i = 0; i <= numPixels - 8; i += 8)
@ -113,12 +110,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (i != numPixels)
{
AddGreenToBlueAndRedScalar(pixelData.Slice((int)i));
AddGreenToBlueAndRedScalar(pixelData[(int)i..]);
}
}
else if (Ssse3.IsSupported)
{
Vector128<byte> addGreenToBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255);
var addGreenToBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255);
int numPixels = pixelData.Length;
nint i;
for (i = 0; i <= numPixels - 4; i += 4)
@ -132,7 +129,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (i != numPixels)
{
AddGreenToBlueAndRedScalar(pixelData.Slice((int)i));
AddGreenToBlueAndRedScalar(pixelData[(int)i..]);
}
}
else if (Sse2.IsSupported)
@ -154,11 +151,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (i != numPixels)
{
AddGreenToBlueAndRedScalar(pixelData.Slice((int)i));
AddGreenToBlueAndRedScalar(pixelData[(int)i..]);
}
}
else
#endif
{
AddGreenToBlueAndRedScalar(pixelData);
}
@ -180,10 +176,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
public static void SubtractGreenFromBlueAndRed(Span<uint> pixelData)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
Vector256<byte> subtractGreenFromBlueAndRedMaskAvx2 = Vector256.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255, 17, 255, 17, 255, 21, 255, 21, 255, 25, 255, 25, 255, 29, 255, 29, 255);
var subtractGreenFromBlueAndRedMaskAvx2 = Vector256.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255, 17, 255, 17, 255, 21, 255, 21, 255, 25, 255, 25, 255, 29, 255, 29, 255);
int numPixels = pixelData.Length;
nint i;
for (i = 0; i <= numPixels - 8; i += 8)
@ -197,12 +192,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (i != numPixels)
{
SubtractGreenFromBlueAndRedScalar(pixelData.Slice((int)i));
SubtractGreenFromBlueAndRedScalar(pixelData[(int)i..]);
}
}
else if (Ssse3.IsSupported)
{
Vector128<byte> subtractGreenFromBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255);
var subtractGreenFromBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255);
int numPixels = pixelData.Length;
nint i;
for (i = 0; i <= numPixels - 4; i += 4)
@ -216,7 +211,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (i != numPixels)
{
SubtractGreenFromBlueAndRedScalar(pixelData.Slice((int)i));
SubtractGreenFromBlueAndRedScalar(pixelData[(int)i..]);
}
}
else if (Sse2.IsSupported)
@ -238,11 +233,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (i != numPixels)
{
SubtractGreenFromBlueAndRedScalar(pixelData.Slice((int)i));
SubtractGreenFromBlueAndRedScalar(pixelData[(int)i..]);
}
}
else
#endif
{
SubtractGreenFromBlueAndRedScalar(pixelData);
}
@ -375,11 +369,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <param name="numPixels">The number of pixels to process.</param>
public static void TransformColor(Vp8LMultipliers m, Span<uint> pixelData, int numPixels)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && numPixels >= 8)
{
Vector256<byte> transformColorAlphaGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
Vector256<byte> transformColorRedBlueMask256 = Vector256.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0);
var transformColorAlphaGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
var transformColorRedBlueMask256 = Vector256.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0);
Vector256<int> multsrb = MkCst32(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue));
Vector256<int> multsb2 = MkCst32(Cst5b(m.RedToBlue), 0);
@ -405,13 +398,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (idx != numPixels)
{
TransformColorScalar(m, pixelData.Slice((int)idx), numPixels - (int)idx);
TransformColorScalar(m, pixelData[(int)idx..], numPixels - (int)idx);
}
}
else if (Sse2.IsSupported)
{
Vector128<byte> transformColorAlphaGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
Vector128<byte> transformColorRedBlueMask = Vector128.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0);
var transformColorAlphaGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
var transformColorRedBlueMask = Vector128.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0);
Vector128<int> multsrb = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue));
Vector128<int> multsb2 = MkCst16(Cst5b(m.RedToBlue), 0);
nint idx;
@ -436,11 +429,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (idx != numPixels)
{
TransformColorScalar(m, pixelData.Slice((int)idx), numPixels - (int)idx);
TransformColorScalar(m, pixelData[(int)idx..], numPixels - (int)idx);
}
}
else
#endif
{
TransformColorScalar(m, pixelData, numPixels);
}
@ -471,10 +463,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <param name="pixelData">The pixel data to apply the inverse transform on.</param>
public static void TransformColorInverse(Vp8LMultipliers m, Span<uint> pixelData)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && pixelData.Length >= 8)
{
Vector256<byte> transformColorInverseAlphaGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
var transformColorInverseAlphaGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
Vector256<int> multsrb = MkCst32(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue));
Vector256<int> multsb2 = MkCst32(Cst5b(m.RedToBlue), 0);
nint idx;
@ -500,12 +491,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (idx != pixelData.Length)
{
TransformColorInverseScalar(m, pixelData.Slice((int)idx));
TransformColorInverseScalar(m, pixelData[(int)idx..]);
}
}
else if (Sse2.IsSupported)
{
Vector128<byte> transformColorInverseAlphaGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
var transformColorInverseAlphaGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255);
Vector128<int> multsrb = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue));
Vector128<int> multsb2 = MkCst16(Cst5b(m.RedToBlue), 0);
@ -532,11 +523,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (idx != pixelData.Length)
{
TransformColorInverseScalar(m, pixelData.Slice((int)idx));
TransformColorInverseScalar(m, pixelData[(int)idx..]);
}
}
else
#endif
{
TransformColorInverseScalar(m, pixelData);
}
@ -753,7 +743,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <returns>Shanon entropy.</returns>
public static float CombinedShannonEntropy(Span<int> x, Span<int> y)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
double retVal = 0.0d;
@ -906,7 +895,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
return (float)retVal;
}
else
#endif
{
double retVal = 0.0d;
uint sumX = 0, sumXY = 0;
@ -1406,7 +1394,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static uint ClampedAddSubtractFull(uint c0, uint c1, uint c2)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
Vector128<byte> c0Vec = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c0).AsByte(), Vector128<byte>.Zero);
@ -1415,10 +1402,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Vector128<short> v1 = Sse2.Add(c0Vec.AsInt16(), c1Vec.AsInt16());
Vector128<short> v2 = Sse2.Subtract(v1, c2Vec.AsInt16());
Vector128<byte> b = Sse2.PackUnsignedSaturate(v2, v2);
uint output = Sse2.ConvertToUInt32(b.AsUInt32());
return output;
return Sse2.ConvertToUInt32(b.AsUInt32());
}
#endif
{
int a = AddSubtractComponentFull(
(int)(c0 >> 24),
@ -1439,7 +1425,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static uint ClampedAddSubtractHalf(uint c0, uint c1, uint c2)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
Vector128<byte> c0Vec = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c0).AsByte(), Vector128<byte>.Zero);
@ -1453,10 +1438,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Vector128<short> a3 = Sse2.ShiftRightArithmetic(a2, 1);
Vector128<short> a4 = Sse2.Add(a0, a3).AsInt16();
Vector128<byte> a5 = Sse2.PackUnsignedSaturate(a4, a4);
uint output = Sse2.ConvertToUInt32(a5.AsUInt32());
return output;
return Sse2.ConvertToUInt32(a5.AsUInt32());
}
#endif
{
uint ave = Average2(c0, c1);
int a = AddSubtractComponentHalf((int)(ave >> 24), (int)(c2 >> 24));
@ -1476,17 +1460,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
[MethodImpl(InliningOptions.ShortMethod)]
private static uint Clip255(uint a) => a < 256 ? a : ~a >> 24;
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector128<int> MkCst16(int hi, int lo) => Vector128.Create((hi << 16) | (lo & 0xffff));
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector256<int> MkCst32(int hi, int lo) => Vector256.Create((hi << 16) | (lo & 0xffff));
#endif
private static uint Select(uint a, uint b, uint c, Span<short> scratch)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
Span<short> output = scratch;
@ -1510,7 +1491,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
}
else
#endif
{
int paMinusPb =
Sub3((int)(a >> 24), (int)(b >> 24), (int)(c >> 24)) +

4
src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs

@ -5,10 +5,8 @@ using System;
using System.Collections.Generic;
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.Lossless
{
@ -517,7 +515,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
DebugGuard.MustBeGreaterThanOrEqualTo(b.Length, count, nameof(b.Length));
DebugGuard.MustBeGreaterThanOrEqualTo(output.Length, count, nameof(output.Length));
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
ref uint aRef = ref MemoryMarshal.GetReference(a);
@ -551,7 +548,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
}
else
#endif
{
for (int i = 0; i < count; i++)
{

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

@ -5,10 +5,8 @@ using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Webp.Lossy
@ -19,7 +17,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
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);
@ -29,17 +26,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
return Vp8_Sse16xN_Sse2(a, b, 8);
}
#endif
{
return Vp8_SseNxN(a, b, 16, 16);
}
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)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
return Vp8_Sse16xN_Avx2(a, b, 2);
@ -49,17 +43,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
return Vp8_Sse16xN_Sse2(a, b, 4);
}
#endif
{
return Vp8_SseNxN(a, b, 16, 8);
}
return Vp8_SseNxN(a, b, 16, 8);
}
// Note: method name in libwebp reference implementation is called VP8SSE4x4.
[MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8_Sse4X4(Span<byte> a, Span<byte> b)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
// Load values.
@ -128,10 +119,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return Numerics.ReduceSum(sum);
}
#endif
{
return Vp8_SseNxN(a, b, 4, 4);
}
return Vp8_SseNxN(a, b, 4, 4);
}
[MethodImpl(InliningOptions.ShortMethod)]
@ -155,7 +144,6 @@ 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)
{
@ -251,7 +239,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return Avx2.Add(sum1, sum2);
}
#endif
[MethodImpl(InliningOptions.ShortMethod)]
public static void Vp8Copy4X4(Span<byte> src, Span<byte> dst) => Copy(src, dst, 4, 4);
@ -274,7 +261,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static int Vp8Disto16X16(Span<byte> a, Span<byte> b, Span<ushort> w, Span<int> scratch)
{
int d = 0;
int dataSize = (4 * WebpConstants.Bps) - 16;
const int dataSize = (4 * WebpConstants.Bps) - 16;
for (int y = 0; y < 16 * WebpConstants.Bps; y += 4 * WebpConstants.Bps)
{
for (int x = 0; x < 16; x += 4)
@ -289,14 +276,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8Disto4X4(Span<byte> a, Span<byte> b, Span<ushort> w, Span<int> scratch)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse41.IsSupported)
{
int diffSum = TTransformSse41(a, b, w);
return Math.Abs(diffSum) >> 5;
}
else
#endif
{
int sum1 = TTransform(a, w, scratch);
int sum2 = TTransform(b, w, scratch);
@ -328,7 +313,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int j = 0; j < 16; j++)
{
// memcpy(dst + j * BPS, dst - BPS, 16);
src.CopyTo(dst.Slice(j * WebpConstants.Bps));
src.CopyTo(dst[(j * WebpConstants.Bps)..]);
}
}
@ -342,7 +327,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
byte v = yuv[offset];
Memset(dst, v, 0, 16);
offset += WebpConstants.Bps;
dst = dst.Slice(WebpConstants.Bps);
dst = dst[WebpConstants.Bps..];
}
}
@ -399,11 +384,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// vertical
Span<byte> src = yuv.Slice(offset - WebpConstants.Bps, 8);
int endIdx = 8 * WebpConstants.Bps;
const int endIdx = 8 * WebpConstants.Bps;
for (int j = 0; j < endIdx; j += WebpConstants.Bps)
{
// memcpy(dst + j * BPS, dst - BPS, 8);
src.CopyTo(dst.Slice(j));
src.CopyTo(dst[j..]);
}
}
@ -417,7 +402,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// dst += BPS;
byte v = yuv[offset];
Memset(dst, v, 0, 8);
dst = dst.Slice(WebpConstants.Bps);
dst = dst[WebpConstants.Bps..];
offset += WebpConstants.Bps;
}
}
@ -427,7 +412,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// DC with no top samples.
int dc0 = 4;
int offsetMinusOne = offset - 1;
int endIdx = 8 * WebpConstants.Bps;
const int endIdx = 8 * WebpConstants.Bps;
for (int i = 0; i < endIdx; i += WebpConstants.Bps)
{
// dc0 += dst[-1 + i * BPS];
@ -466,7 +451,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
dc >>= 3;
int endIndx = 4 * WebpConstants.Bps;
const int endIndx = 4 * WebpConstants.Bps;
for (int i = 0; i < endIndx; i += WebpConstants.Bps)
{
Memset(dst, (byte)dc, i, 4);
@ -484,10 +469,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
vals[1] = Avg3(yuv[topOffset], yuv[topOffset + 1], yuv[topOffset + 2]);
vals[2] = Avg3(yuv[topOffset + 1], yuv[topOffset + 2], yuv[topOffset + 3]);
vals[3] = Avg3(yuv[topOffset + 2], yuv[topOffset + 3], yuv[topOffset + 4]);
int endIdx = 4 * WebpConstants.Bps;
const int endIdx = 4 * WebpConstants.Bps;
for (int i = 0; i < endIdx; i += WebpConstants.Bps)
{
vals.CopyTo(dst.Slice(i));
vals.CopyTo(dst[i..]);
}
}
@ -503,11 +488,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
uint val = 0x01010101U * Avg3(a, b, c);
BinaryPrimitives.WriteUInt32BigEndian(dst, val);
val = 0x01010101U * Avg3(b, c, d);
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(WebpConstants.Bps), val);
BinaryPrimitives.WriteUInt32BigEndian(dst[WebpConstants.Bps..], val);
val = 0x01010101U * Avg3(c, d, e);
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(2 * WebpConstants.Bps), val);
BinaryPrimitives.WriteUInt32BigEndian(dst[(2 * WebpConstants.Bps)..], val);
val = 0x01010101U * Avg3(d, e, e);
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(3 * WebpConstants.Bps), val);
BinaryPrimitives.WriteUInt32BigEndian(dst[(3 * WebpConstants.Bps)..], val);
}
public static void RD4(Span<byte> dst, Span<byte> yuv, int offset)
@ -726,7 +711,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// </summary>
public static void TransformWht(Span<short> input, Span<short> output, Span<int> scratch)
{
Span<int> tmp = scratch.Slice(0, 16);
Span<int> tmp = scratch[..16];
tmp.Clear();
for (int i = 0; i < 4; i++)
{
@ -768,7 +753,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static int TTransform(Span<byte> input, Span<ushort> w, Span<int> scratch)
{
int sum = 0;
Span<int> tmp = scratch.Slice(0, 16);
Span<int> tmp = scratch[..16];
tmp.Clear();
// horizontal pass.
@ -807,13 +792,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
sum += w[8] * Math.Abs(b2);
sum += w[12] * Math.Abs(b3);
w = w.Slice(1);
w = w[1..];
}
return sum;
}
#if SUPPORTS_RUNTIME_INTRINSICS
/// <summary>
/// Hadamard transform
/// Returns the weighted sum of the absolute value of transformed coefficients.
@ -944,13 +928,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// a02 a12 a22 a32 b02 b12 b22 b32
// a03 a13 a23 a33 b03 b13 b23 b33
}
#endif
// Transforms (Paragraph 14.4).
// Does two transforms.
public static void TransformTwo(Span<short> src, Span<byte> dst, Span<int> scratch)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
// This implementation makes use of 16-bit fixed point versions of two
@ -1002,8 +984,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Vector128<short> a = Sse2.Add(in0.AsInt16(), in2.AsInt16());
Vector128<short> b = Sse2.Subtract(in0.AsInt16(), in2.AsInt16());
Vector128<short> k1 = Vector128.Create((short)20091);
Vector128<short> k2 = Vector128.Create((short)-30068);
var k1 = Vector128.Create((short)20091);
var k2 = Vector128.Create((short)-30068);
// c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
Vector128<short> c1 = Sse2.MultiplyHigh(in1.AsInt16(), k2);
@ -1097,16 +1079,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Unsafe.As<byte, Vector64<byte>>(ref Unsafe.Add(ref outputRef, WebpConstants.Bps * 3)) = dst3.GetLower();
}
else
#endif
{
TransformOne(src, dst, scratch);
TransformOne(src.Slice(16), dst.Slice(4), scratch);
TransformOne(src[16..], dst[4..], scratch);
}
}
public static void TransformOne(Span<short> src, Span<byte> dst, Span<int> scratch)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
// Load and concatenate the transform coefficients.
@ -1126,8 +1106,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Vector128<short> a = Sse2.Add(in0.AsInt16(), in2.AsInt16());
Vector128<short> b = Sse2.Subtract(in0.AsInt16(), in2.AsInt16());
Vector128<short> k1 = Vector128.Create((short)20091);
Vector128<short> k2 = Vector128.Create((short)-30068);
var k1 = Vector128.Create((short)20091);
var k2 = Vector128.Create((short)-30068);
// c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
Vector128<short> c1 = Sse2.MultiplyHigh(in1.AsInt16(), k2);
@ -1225,9 +1205,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Unsafe.As<byte, int>(ref Unsafe.Add(ref outputRef, WebpConstants.Bps * 3)) = output3;
}
else
#endif
{
Span<int> tmp = scratch.Slice(0, 16);
Span<int> tmp = scratch[..16];
int tmpOffset = 0;
for (int srcOffset = 0; srcOffset < 4; srcOffset++)
{
@ -1263,10 +1242,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int b = dc - tmp[tmpOffsetPlus8];
int c = Mul2(tmp[tmpOffsetPlus4]) - Mul1(tmp[tmpOffsetPlus12]);
int d = Mul1(tmp[tmpOffsetPlus4]) + Mul2(tmp[tmpOffsetPlus12]);
Store(dst.Slice(dstOffset), 0, 0, a + d);
Store(dst.Slice(dstOffset), 1, 0, b + c);
Store(dst.Slice(dstOffset), 2, 0, b - c);
Store(dst.Slice(dstOffset), 3, 0, a - d);
Store(dst[dstOffset..], 0, 0, a + d);
Store(dst[dstOffset..], 1, 0, b + c);
Store(dst[dstOffset..], 2, 0, b - c);
Store(dst[dstOffset..], 3, 0, a - d);
tmpOffset++;
dstOffset += WebpConstants.Bps;
@ -1302,37 +1281,36 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void TransformUv(Span<short> src, Span<byte> dst, Span<int> scratch)
{
TransformTwo(src.Slice(0 * 16), dst, scratch);
TransformTwo(src.Slice(2 * 16), dst.Slice(4 * WebpConstants.Bps), scratch);
TransformTwo(src[..], dst, scratch);
TransformTwo(src[(2 * 16)..], dst[(4 * WebpConstants.Bps)..], scratch);
}
public static void TransformDcuv(Span<short> src, Span<byte> dst)
{
if (src[0 * 16] != 0)
{
TransformDc(src.Slice(0 * 16), dst);
TransformDc(src[..], dst);
}
if (src[1 * 16] != 0)
{
TransformDc(src.Slice(1 * 16), dst.Slice(4));
TransformDc(src[(1 * 16)..], dst[4..]);
}
if (src[2 * 16] != 0)
{
TransformDc(src.Slice(2 * 16), dst.Slice(4 * WebpConstants.Bps));
TransformDc(src[(2 * 16)..], dst[(4 * WebpConstants.Bps)..]);
}
if (src[3 * 16] != 0)
{
TransformDc(src.Slice(3 * 16), dst.Slice((4 * WebpConstants.Bps) + 4));
TransformDc(src[(3 * 16)..], dst[((4 * WebpConstants.Bps) + 4)..]);
}
}
// Simple In-loop filtering (Paragraph 15.2)
public static void SimpleVFilter16(Span<byte> p, int offset, int stride, int thresh)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
// Load.
@ -1351,7 +1329,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Unsafe.As<byte, Vector128<sbyte>>(ref outputRef) = q0.AsSByte();
}
else
#endif
{
int thresh2 = (2 * thresh) + 1;
int end = 16 + offset;
@ -1367,7 +1344,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void SimpleHFilter16(Span<byte> p, int offset, int stride, int thresh)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
// Beginning of p1
@ -1378,7 +1354,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Store16x4(p1, p0, q0, q1, ref pRef, ref Unsafe.Add(ref pRef, 8 * stride), stride);
}
else
#endif
{
int thresh2 = (2 * thresh) + 1;
int end = offset + (16 * stride);
@ -1394,7 +1369,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void SimpleVFilter16i(Span<byte> p, int offset, int stride, int thresh)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
for (int k = 3; k > 0; k--)
@ -1404,7 +1378,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
else
#endif
{
for (int k = 3; k > 0; k--)
{
@ -1416,7 +1389,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void SimpleHFilter16i(Span<byte> p, int offset, int stride, int thresh)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
for (int k = 3; k > 0; k--)
@ -1426,7 +1398,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
else
#endif
{
for (int k = 3; k > 0; k--)
{
@ -1440,7 +1411,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
public static void VFilter16(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
ref byte pRef = ref MemoryMarshal.GetReference(p);
@ -1475,7 +1445,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Unsafe.As<byte, Vector128<int>>(ref Unsafe.Add(ref outputRef, offset + (2 * stride))) = q2.AsInt32();
}
else
#endif
{
FilterLoop26(p, offset, stride, 1, 16, thresh, ithresh, hevThresh);
}
@ -1484,7 +1453,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
public static void HFilter16(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
ref byte pRef = ref MemoryMarshal.GetReference(p);
@ -1508,7 +1476,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Store16x4(q0, q1, q2, q3, ref Unsafe.Add(ref pRef, offset), ref Unsafe.Add(ref pRef, offset + (8 * stride)), stride);
}
else
#endif
{
FilterLoop26(p, offset, 1, stride, 16, thresh, ithresh, hevThresh);
}
@ -1516,7 +1483,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void VFilter16i(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
ref byte pRef = ref MemoryMarshal.GetReference(p);
@ -1528,7 +1494,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int k = 3; k > 0; k--)
{
// Beginning of p1.
Span<byte> b = p.Slice(offset + (2 * stride));
Span<byte> b = p[(offset + (2 * stride))..];
offset += 4 * stride;
Vector128<byte> mask = Abs(p0, p1);
@ -1562,7 +1528,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
else
#endif
{
for (int k = 3; k > 0; k--)
{
@ -1574,7 +1539,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void HFilter16i(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
ref byte pRef = ref MemoryMarshal.GetReference(p);
@ -1611,7 +1575,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
else
#endif
{
for (int k = 3; k > 0; k--)
{
@ -1625,7 +1588,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
public static void VFilter8(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
// Load uv h-edges.
@ -1661,7 +1623,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
StoreUv(q2, ref uRef, ref vRef, offset + (2 * stride));
}
else
#endif
{
FilterLoop26(u, offset, stride, 1, 8, thresh, ithresh, hevThresh);
FilterLoop26(v, offset, stride, 1, 8, thresh, ithresh, hevThresh);
@ -1671,7 +1632,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
public static void HFilter8(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
ref byte uRef = ref MemoryMarshal.GetReference(u);
@ -1695,7 +1655,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Store16x4(q0, q1, q2, q3, ref Unsafe.Add(ref uRef, offset), ref Unsafe.Add(ref vRef, offset), stride);
}
else
#endif
{
FilterLoop26(u, offset, 1, stride, 8, thresh, ithresh, hevThresh);
FilterLoop26(v, offset, 1, stride, 8, thresh, ithresh, hevThresh);
@ -1705,7 +1664,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
public static void VFilter8i(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
// Load uv h-edges.
@ -1741,7 +1699,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
StoreUv(q1, ref uRef, ref vRef, offset + stride);
}
else
#endif
{
int offset4mulstride = offset + (4 * stride);
FilterLoop24(u, offset4mulstride, stride, 1, 8, thresh, ithresh, hevThresh);
@ -1752,7 +1709,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
public static void HFilter8i(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
ref byte uRef = ref MemoryMarshal.GetReference(u);
@ -1780,7 +1736,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Store16x4(p1, p0, q0, q1, ref Unsafe.Add(ref uRef, offset), ref Unsafe.Add(ref vRef, offset), stride);
}
else
#endif
{
int offsetPlus4 = offset + 4;
FilterLoop24(u, offsetPlus4, 1, stride, 8, thresh, ithresh, hevThresh);
@ -1790,7 +1745,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void Mean16x4(Span<byte> input, Span<uint> dc)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Ssse3.IsSupported)
{
Vector128<byte> mean16x4Mask = Vector128.Create((short)0x00ff).AsByte();
@ -1821,7 +1775,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Unsafe.As<uint, Vector128<uint>>(ref outputRef) = wide;
}
else
#endif
{
for (int k = 0; k < 4; k++)
{
@ -1835,7 +1788,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
dc[k] = avg;
input = input.Slice(4); // go to next 4x4 block.
input = input[4..]; // go to next 4x4 block.
}
}
}
@ -1860,7 +1813,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
for (int j = 0; j < 16; j++)
{
Memset(dst.Slice(j * WebpConstants.Bps), (byte)v, 0, 16);
Memset(dst[(j * WebpConstants.Bps)..], (byte)v, 0, 16);
}
}
@ -1868,7 +1821,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
// For information about how true motion works, see rfc6386, page 52. ff and section 20.14.
int topOffset = offset - WebpConstants.Bps;
Span<byte> top = yuv.Slice(topOffset);
Span<byte> top = yuv[topOffset..];
byte p = yuv[topOffset - 1];
int leftOffset = offset - 1;
byte left = yuv[leftOffset];
@ -1881,7 +1834,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
leftOffset += WebpConstants.Bps;
left = yuv[leftOffset];
dst = dst.Slice(WebpConstants.Bps);
dst = dst[WebpConstants.Bps..];
}
}
@ -1959,11 +1912,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
p[offset] = WebpLookupTables.Clip1(q0 - a1);
}
#if SUPPORTS_RUNTIME_INTRINSICS
// Applies filter on 2 pixels (p0 and q0)
private static void DoFilter2Sse2(ref Vector128<byte> p1, ref Vector128<byte> p0, ref Vector128<byte> q0, ref Vector128<byte> q1, int thresh)
{
Vector128<byte> signBit = Vector128.Create((byte)0x80);
var signBit = Vector128.Create((byte)0x80);
// Convert p1/q1 to byte (for GetBaseDelta).
Vector128<byte> p1s = Sse2.Xor(p1, signBit);
@ -1992,7 +1944,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Compute hev mask.
Vector128<byte> notHev = GetNotHev(ref p1, ref p0, ref q0, ref q1, tresh);
Vector128<byte> signBit = Vector128.Create((byte)0x80);
var signBit = Vector128.Create((byte)0x80);
// Convert to signed values.
p1 = Sse2.Xor(p1, signBit);
@ -2036,7 +1988,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Vector128<byte> notHev = GetNotHev(ref p1, ref p0, ref q0, ref q1, tresh);
// Convert to signed values.
Vector128<byte> signBit = Vector128.Create((byte)0x80);
var signBit = Vector128.Create((byte)0x80);
p1 = Sse2.Xor(p1, signBit);
p0 = Sse2.Xor(p0, signBit);
q0 = Sse2.Xor(q0, signBit);
@ -2057,11 +2009,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Vector128<byte> flow = Sse2.UnpackLow(Vector128<byte>.Zero, f);
Vector128<byte> fhigh = Sse2.UnpackHigh(Vector128<byte>.Zero, f);
Vector128<short> nine = Vector128.Create((short)0x0900);
var nine = Vector128.Create((short)0x0900);
Vector128<short> f9Low = Sse2.MultiplyHigh(flow.AsInt16(), nine); // Filter (lo) * 9
Vector128<short> f9High = Sse2.MultiplyHigh(fhigh.AsInt16(), nine); // Filter (hi) * 9
Vector128<short> sixtyThree = Vector128.Create((short)63);
var sixtyThree = Vector128.Create((short)63);
Vector128<short> a2Low = Sse2.Add(f9Low, sixtyThree); // Filter * 9 + 63
Vector128<short> a2High = Sse2.Add(f9High, sixtyThree); // Filter * 9 + 63
@ -2100,7 +2052,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// not_hev <= t1 && not_hev <= t2
return Sse2.CompareEqual(tMaxH, Vector128<byte>.Zero);
}
#endif
// Applies filter on 4 pixels (p1, p0, q0 and q1)
private static void DoFilter4(Span<byte> p, int offset, int step)
@ -2180,7 +2131,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
WebpLookupTables.Abs0(q2 - q1) <= it && WebpLookupTables.Abs0(q1 - q0) <= it;
}
#if SUPPORTS_RUNTIME_INTRINSICS
private static Vector128<byte> NeedsFilter(Vector128<byte> p1, Vector128<byte> p0, Vector128<byte> q0, Vector128<byte> q1, int thresh)
{
var mthresh = Vector128.Create((byte)thresh);
@ -2345,7 +2295,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Pixels 'pi' and 'qi' are int8_t on input, uint8_t on output (sign flip).
private static void Update2Pixels(ref Vector128<byte> pi, ref Vector128<byte> qi, Vector128<short> a0Low, Vector128<short> a0High)
{
Vector128<byte> signBit = Vector128.Create((byte)0x80);
var signBit = Vector128.Create((byte)0x80);
Vector128<short> a1Low = Sse2.ShiftRightArithmetic(a0Low, 7);
Vector128<short> a1High = Sse2.ShiftRightArithmetic(a0High, 7);
Vector128<sbyte> delta = Sse2.PackSignedSaturate(a1Low, a1High);
@ -2374,7 +2324,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector128<byte> Abs(Vector128<byte> p, Vector128<byte> q)
=> Sse2.Or(Sse2.SubtractSaturate(q, p), Sse2.SubtractSaturate(p, q));
#endif
[MethodImpl(InliningOptions.ShortMethod)]
private static bool Hev(Span<byte> p, int offset, int step, int thresh)
@ -2411,7 +2360,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
private static void Put8x8uv(byte value, Span<byte> dst)
{
int end = 8 * WebpConstants.Bps;
const int end = 8 * WebpConstants.Bps;
for (int j = 0; j < end; j += WebpConstants.Bps)
{
// memset(dst + j * BPS, value, 8);

42
src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs

@ -4,10 +4,8 @@
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
{
@ -77,9 +75,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (mode == 0 || rdCur.Score < rdBest.Score)
{
Vp8ModeScore tmp = rdCur;
rdCur = rdBest;
rdBest = tmp;
(rdBest, rdCur) = (rdCur, rdBest);
it.SwapOut();
}
}
@ -132,9 +128,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
rdi4.Clear();
int mode;
int bestMode = -1;
Span<byte> src = src0.Slice(WebpLookupTables.Vp8Scan[it.I4]);
Span<byte> src = src0[WebpLookupTables.Vp8Scan[it.I4]..];
short[] modeCosts = it.GetCostModeI4(rd.ModesI4);
Span<byte> bestBlock = bestBlocks.Slice(WebpLookupTables.Vp8Scan[it.I4]);
Span<byte> bestBlock = bestBlocks[WebpLookupTables.Vp8Scan[it.I4]..];
Span<byte> tmpDst = it.Scratch.AsSpan();
tmpDst.Clear();
@ -199,7 +195,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
// Copy selected samples to the right place.
LossyUtils.Vp8Copy4X4(bestBlock, bestBlocks.Slice(WebpLookupTables.Vp8Scan[it.I4]));
LossyUtils.Vp8Copy4X4(bestBlock, bestBlocks[WebpLookupTables.Vp8Scan[it.I4]..]);
rd.ModesI4[it.I4] = (byte)bestMode;
it.TopNz[it.I4 & 3] = it.LeftNz[it.I4 >> 2] = rdi4.Nz != 0 ? 1 : 0;
@ -290,14 +286,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<int> scratch = it.Scratch3.AsSpan(0, 16);
shortScratchSpan.Clear();
scratch.Clear();
Span<short> dcTmp = shortScratchSpan.Slice(0, 16);
Span<short> dcTmp = shortScratchSpan[..16];
Span<short> tmp = shortScratchSpan.Slice(16, 16 * 16);
for (n = 0; n < 16; n += 2)
{
Vp8Encoding.FTransform2(
src.Slice(WebpLookupTables.Vp8Scan[n]),
reference.Slice(WebpLookupTables.Vp8Scan[n]),
src[WebpLookupTables.Vp8Scan[n]..],
reference[WebpLookupTables.Vp8Scan[n]..],
tmp.Slice(n * 16, 16),
tmp.Slice((n + 1) * 16, 16),
scratch);
@ -318,7 +314,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
LossyUtils.TransformWht(dcTmp, tmp, scratch);
for (n = 0; n < 16; n += 2)
{
Vp8Encoding.ITransformTwo(reference.Slice(WebpLookupTables.Vp8Scan[n]), tmp.Slice(n * 16, 32), yuvOut.Slice(WebpLookupTables.Vp8Scan[n]), scratch);
Vp8Encoding.ITransformTwo(reference[WebpLookupTables.Vp8Scan[n]..], tmp.Slice(n * 16, 32), yuvOut[WebpLookupTables.Vp8Scan[n]..], scratch);
}
return nz;
@ -348,8 +344,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (n = 0; n < 8; n += 2)
{
Vp8Encoding.FTransform2(
src.Slice(WebpLookupTables.Vp8ScanUv[n]),
reference.Slice(WebpLookupTables.Vp8ScanUv[n]),
src[WebpLookupTables.Vp8ScanUv[n]..],
reference[WebpLookupTables.Vp8ScanUv[n]..],
tmp.Slice(n * 16, 16),
tmp.Slice((n + 1) * 16, 16),
scratch);
@ -364,7 +360,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (n = 0; n < 8; n += 2)
{
Vp8Encoding.ITransformTwo(reference.Slice(WebpLookupTables.Vp8ScanUv[n]), tmp.Slice(n * 16, 32), yuvOut.Slice(WebpLookupTables.Vp8ScanUv[n]), scratch);
Vp8Encoding.ITransformTwo(reference[WebpLookupTables.Vp8ScanUv[n]..], tmp.Slice(n * 16, 32), yuvOut[WebpLookupTables.Vp8ScanUv[n]..], scratch);
}
return nz << 16;
@ -512,14 +508,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
public static int Quantize2Blocks(Span<short> input, Span<short> output, ref Vp8Matrix mtx)
{
int nz = QuantizeBlock(input.Slice(0, 16), output.Slice(0, 16), ref mtx) << 0;
int nz = QuantizeBlock(input[..16], output[..16], ref mtx) << 0;
nz |= QuantizeBlock(input.Slice(1 * 16, 16), output.Slice(1 * 16, 16), ref mtx) << 1;
return nz;
}
public static int QuantizeBlock(Span<short> input, Span<short> output, ref Vp8Matrix mtx)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
// Load all inputs.
@ -636,7 +631,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Vector128<short> out8 = Sse2.PackSignedSaturate(out08.AsInt32(), out12.AsInt32());
// if (coeff > 2047) coeff = 2047
Vector128<short> maxCoeff2047 = Vector128.Create((short)MaxLevel);
var maxCoeff2047 = Vector128.Create((short)MaxLevel);
out0 = Sse2.Min(out0, maxCoeff2047);
out8 = Sse2.Min(out8, maxCoeff2047);
@ -677,7 +672,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return Sse2.MoveMask(cmpeq) != 0xffff ? 1 : 0;
}
else
#endif
{
int last = -1;
int n;
@ -762,11 +756,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
c[0] += (short)(((C1 * top[0]) + (C2 * left[0])) >> (DSHIFT - DSCALE));
int err0 = QuantizeSingle(c, ref mtx);
c[1 * 16] += (short)(((C1 * top[1]) + (C2 * err0)) >> (DSHIFT - DSCALE));
int err1 = QuantizeSingle(c.Slice(1 * 16), ref mtx);
int err1 = QuantizeSingle(c[(1 * 16)..], ref mtx);
c[2 * 16] += (short)(((C1 * err0) + (C2 * left[1])) >> (DSHIFT - DSCALE));
int err2 = QuantizeSingle(c.Slice(2 * 16), ref mtx);
int err2 = QuantizeSingle(c[(2 * 16)..], ref mtx);
c[3 * 16] += (short)(((C1 * err1) + (C2 * err2)) >> (DSHIFT - DSCALE));
int err3 = QuantizeSingle(c.Slice(3 * 16), ref mtx);
int err3 = QuantizeSingle(c[(3 * 16)..], ref mtx);
rd.Derr[ch, 0] = err1;
rd.Derr[ch, 1] = err2;
@ -781,13 +775,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> vSpan = BitConverter.GetBytes(v).AsSpan();
for (nint i = 0; i < 16; i++)
{
if (!src.Slice(0, 4).SequenceEqual(vSpan) || !src.Slice(4, 4).SequenceEqual(vSpan) ||
if (!src[..4].SequenceEqual(vSpan) || !src.Slice(4, 4).SequenceEqual(vSpan) ||
!src.Slice(8, 4).SequenceEqual(vSpan) || !src.Slice(12, 4).SequenceEqual(vSpan))
{
return false;
}
src = src.Slice(WebpConstants.Bps);
src = src[WebpConstants.Bps..];
}
return true;

124
src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs

@ -5,10 +5,8 @@ using System;
using System.Buffers.Binary;
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
{
@ -81,7 +79,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Does two inverse transforms.
public static void ITransformTwo(Span<byte> reference, Span<short> input, Span<byte> dst, Span<int> scratch)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
// This implementation makes use of 16-bit fixed point versions of two
@ -184,16 +181,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Unsafe.As<byte, Vector64<byte>>(ref Unsafe.Add(ref outputRef, WebpConstants.Bps * 3)) = ref3.GetLower();
}
else
#endif
{
ITransformOne(reference, input, dst, scratch);
ITransformOne(reference.Slice(4), input.Slice(16), dst.Slice(4), scratch);
ITransformOne(reference[4..], input[16..], dst[4..], scratch);
}
}
public static void ITransformOne(Span<byte> reference, Span<short> input, Span<byte> dst, Span<int> scratch)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
// Load and concatenate the transform coefficients (we'll do two inverse
@ -272,10 +267,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Unsafe.As<byte, int>(ref Unsafe.Add(ref outputRef, WebpConstants.Bps * 3)) = output3;
}
else
#endif
{
int i;
Span<int> tmp = scratch.Slice(0, 16);
Span<int> tmp = scratch[..16];
for (i = 0; i < 4; i++)
{
// vertical pass.
@ -287,8 +281,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
tmp[1] = b + c;
tmp[2] = b - c;
tmp[3] = a - d;
tmp = tmp.Slice(4);
input = input.Slice(1);
tmp = tmp[4..];
input = input[1..];
}
tmp = scratch;
@ -304,12 +298,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Store(dst, reference, 1, i, b + c);
Store(dst, reference, 2, i, b - c);
Store(dst, reference, 3, i, a - d);
tmp = tmp.Slice(1);
tmp = tmp[1..];
}
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
private static void InverseTransformVerticalPass(Vector128<long> in0, Vector128<long> in2, Vector128<long> in1, Vector128<long> in3, out Vector128<short> tmp0, out Vector128<short> tmp1, out Vector128<short> tmp2, out Vector128<short> tmp3)
{
Vector128<short> a = Sse2.Add(in0.AsInt16(), in2.AsInt16());
@ -372,11 +365,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
shifted2 = Sse2.ShiftRightArithmetic(tmp2, 3);
shifted3 = Sse2.ShiftRightArithmetic(tmp3, 3);
}
#endif
public static void FTransform2(Span<byte> src, Span<byte> reference, Span<short> output, Span<short> output2, Span<int> scratch)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
ref byte srcRef = ref MemoryMarshal.GetReference(src);
@ -429,16 +420,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
FTransformPass2SSE2(v01h, v32h, output2);
}
else
#endif
{
FTransform(src, reference, output, scratch);
FTransform(src.Slice(4), reference.Slice(4), output2, scratch);
FTransform(src[4..], reference[4..], output2, scratch);
}
}
public static void FTransform(Span<byte> src, Span<byte> reference, Span<short> output, Span<int> scratch)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
ref byte srcRef = ref MemoryMarshal.GetReference(src);
@ -486,10 +475,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
FTransformPass2SSE2(v01, v32, output);
}
else
#endif
{
int i;
Span<int> tmp = scratch.Slice(0, 16);
Span<int> tmp = scratch[..16];
int srcIdx = 0;
int refIdx = 0;
@ -530,7 +518,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
public static void FTransformPass1SSE2(Vector128<short> row01, Vector128<short> row23, out Vector128<int> out01, out Vector128<int> out32)
{
// *in01 = 00 01 10 11 02 03 12 13
@ -621,11 +608,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Unsafe.As<short, Vector128<short>>(ref outputRef) = d0g1.AsInt16();
Unsafe.As<short, Vector128<short>>(ref Unsafe.Add(ref outputRef, 8)) = d2f3.AsInt16();
}
#endif
public static void FTransformWht(Span<short> input, Span<short> output, Span<int> scratch)
{
Span<int> tmp = scratch.Slice(0, 16);
Span<int> tmp = scratch[..16];
int i;
int inputIdx = 0;
@ -668,62 +654,62 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// luma 16x16 prediction (paragraph 12.3).
public static void EncPredLuma16(Span<byte> dst, Span<byte> left, Span<byte> top)
{
DcMode(dst.Slice(I16DC16), left, top, 16, 16, 5);
VerticalPred(dst.Slice(I16VE16), top, 16);
HorizontalPred(dst.Slice(I16HE16), left, 16);
TrueMotion(dst.Slice(I16TM16), left, top, 16);
DcMode(dst[..], left, top, 16, 16, 5);
VerticalPred(dst[I16VE16..], top, 16);
HorizontalPred(dst[I16HE16..], left, 16);
TrueMotion(dst[I16TM16..], left, top, 16);
}
// Chroma 8x8 prediction (paragraph 12.2).
public static void EncPredChroma8(Span<byte> dst, Span<byte> left, Span<byte> top)
{
// U block.
DcMode(dst.Slice(C8DC8), left, top, 8, 8, 4);
VerticalPred(dst.Slice(C8VE8), top, 8);
HorizontalPred(dst.Slice(C8HE8), left, 8);
TrueMotion(dst.Slice(C8TM8), left, top, 8);
DcMode(dst[C8DC8..], left, top, 8, 8, 4);
VerticalPred(dst[C8VE8..], top, 8);
HorizontalPred(dst[C8HE8..], left, 8);
TrueMotion(dst[C8TM8..], left, top, 8);
// V block.
dst = dst.Slice(8);
if (top != null)
dst = dst[8..];
if (top != default)
{
top = top.Slice(8);
top = top[8..];
}
if (left != null)
if (left != default)
{
left = left.Slice(16);
left = left[16..];
}
DcMode(dst.Slice(C8DC8), left, top, 8, 8, 4);
VerticalPred(dst.Slice(C8VE8), top, 8);
HorizontalPred(dst.Slice(C8HE8), left, 8);
TrueMotion(dst.Slice(C8TM8), left, top, 8);
DcMode(dst[C8DC8..], left, top, 8, 8, 4);
VerticalPred(dst[C8VE8..], top, 8);
HorizontalPred(dst[C8HE8..], left, 8);
TrueMotion(dst[C8TM8..], left, top, 8);
}
// Left samples are top[-5 .. -2], top_left is top[-1], top are
// located at top[0..3], and top right is top[4..7]
public static void EncPredLuma4(Span<byte> dst, Span<byte> top, int topOffset, Span<byte> vals)
{
Dc4(dst.Slice(I4DC4), top, topOffset);
Tm4(dst.Slice(I4TM4), top, topOffset);
Ve4(dst.Slice(I4VE4), top, topOffset, vals);
He4(dst.Slice(I4HE4), top, topOffset);
Rd4(dst.Slice(I4RD4), top, topOffset);
Vr4(dst.Slice(I4VR4), top, topOffset);
Ld4(dst.Slice(I4LD4), top, topOffset);
Vl4(dst.Slice(I4VL4), top, topOffset);
Hd4(dst.Slice(I4HD4), top, topOffset);
Hu4(dst.Slice(I4HU4), top, topOffset);
Dc4(dst[I4DC4..], top, topOffset);
Tm4(dst[I4TM4..], top, topOffset);
Ve4(dst[I4VE4..], top, topOffset, vals);
He4(dst[I4HE4..], top, topOffset);
Rd4(dst[I4RD4..], top, topOffset);
Vr4(dst[I4VR4..], top, topOffset);
Ld4(dst[I4LD4..], top, topOffset);
Vl4(dst[I4VL4..], top, topOffset);
Hd4(dst[I4HD4..], top, topOffset);
Hu4(dst[I4HU4..], top, topOffset);
}
private static void VerticalPred(Span<byte> dst, Span<byte> top, int size)
{
if (top != null)
if (top != default)
{
for (int j = 0; j < size; j++)
{
top.Slice(0, size).CopyTo(dst.Slice(j * WebpConstants.Bps));
top[..size].CopyTo(dst[(j * WebpConstants.Bps)..]);
}
}
else
@ -734,9 +720,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void HorizontalPred(Span<byte> dst, Span<byte> left, int size)
{
if (left != null)
if (left != default)
{
left = left.Slice(1); // in the reference implementation, left starts at - 1.
left = left[1..]; // in the reference implementation, left starts at - 1.
for (int j = 0; j < size; j++)
{
dst.Slice(j * WebpConstants.Bps, size).Fill(left[j]);
@ -750,20 +736,20 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void TrueMotion(Span<byte> dst, Span<byte> left, Span<byte> top, int size)
{
if (left != null)
if (left != default)
{
if (top != null)
if (top != default)
{
Span<byte> clip = Clip1.AsSpan(255 - left[0]); // left [0] instead of left[-1], original left starts at -1
for (int y = 0; y < size; y++)
{
Span<byte> clipTable = clip.Slice(left[y + 1]); // left[y]
Span<byte> clipTable = clip[left[y + 1]..]; // left[y]
for (int x = 0; x < size; x++)
{
dst[x] = clipTable[top[x]];
}
dst = dst.Slice(WebpConstants.Bps);
dst = dst[WebpConstants.Bps..];
}
}
else
@ -777,7 +763,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// is equivalent to VE prediction where you just copy the top samples.
// Note that if top samples are not available, the default value is
// then 129, and not 127 as in the VerticalPred case.
if (top != null)
if (top != default)
{
VerticalPred(dst, top, size);
}
@ -792,17 +778,17 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
int dc = 0;
int j;
if (top != null)
if (top != default)
{
for (j = 0; j < size; j++)
{
dc += top[j];
}
if (left != null)
if (left != default)
{
// top and left present.
left = left.Slice(1); // in the reference implementation, left starts at -1.
left = left[1..]; // in the reference implementation, left starts at -1.
for (j = 0; j < size; j++)
{
dc += left[j];
@ -816,10 +802,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
dc = (dc + round) >> shift;
}
else if (left != null)
else if (left != default)
{
// left but no top.
left = left.Slice(1); // in the reference implementation, left starts at -1.
left = left[1..]; // in the reference implementation, left starts at -1.
for (j = 0; j < size; j++)
{
dc += left[j];
@ -854,13 +840,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> clip = Clip1.AsSpan(255 - top[topOffset - 1]);
for (int y = 0; y < 4; y++)
{
Span<byte> clipTable = clip.Slice(top[topOffset - 2 - y]);
Span<byte> clipTable = clip[top[topOffset - 2 - y]..];
for (int x = 0; x < 4; x++)
{
dst[x] = clipTable[top[topOffset + x]];
}
dst = dst.Slice(WebpConstants.Bps);
dst = dst[WebpConstants.Bps..];
}
}
@ -873,7 +859,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
vals[3] = LossyUtils.Avg3(top[topOffset + 2], top[topOffset + 3], top[topOffset + 4]);
for (int i = 0; i < 4; i++)
{
vals.CopyTo(dst.Slice(i * WebpConstants.Bps));
vals.CopyTo(dst[(i * WebpConstants.Bps)..]);
}
}
@ -889,11 +875,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
uint val = 0x01010101U * LossyUtils.Avg3(x, i, j);
BinaryPrimitives.WriteUInt32BigEndian(dst, val);
val = 0x01010101U * LossyUtils.Avg3(i, j, k);
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(1 * WebpConstants.Bps), val);
BinaryPrimitives.WriteUInt32BigEndian(dst[(1 * WebpConstants.Bps)..], val);
val = 0x01010101U * LossyUtils.Avg3(j, k, l);
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(2 * WebpConstants.Bps), val);
BinaryPrimitives.WriteUInt32BigEndian(dst[(2 * WebpConstants.Bps)..], val);
val = 0x01010101U * LossyUtils.Avg3(k, l, l);
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(3 * WebpConstants.Bps), val);
BinaryPrimitives.WriteUInt32BigEndian(dst[(3 * WebpConstants.Bps)..], val);
}
private static void Rd4(Span<byte> dst, Span<byte> top, int topOffset)

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

@ -4,10 +4,8 @@
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
{
@ -54,10 +52,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.distribution.AsSpan().Clear();
for (j = startBlock; j < endBlock; j++)
{
Vp8Encoding.FTransform(reference.Slice(WebpLookupTables.Vp8DspScan[j]), pred.Slice(WebpLookupTables.Vp8DspScan[j]), this.output, this.scratch);
Vp8Encoding.FTransform(reference[WebpLookupTables.Vp8DspScan[j]..], pred[WebpLookupTables.Vp8DspScan[j]..], this.output, this.scratch);
// Convert coefficients to bin.
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
// Load.
@ -81,7 +78,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
else
#endif
{
for (int k = 0; k < 16; ++k)
{

25
src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs

@ -4,10 +4,8 @@
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
{
@ -46,7 +44,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public void SetCoeffs(Span<short> coeffs)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported)
{
ref short coeffsRef = ref MemoryMarshal.GetReference(coeffs);
@ -68,7 +65,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.Last = mask != 0 ? Numerics.Log2(mask) : -1;
}
else
#endif
{
int n;
this.Last = -1;
@ -82,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
coeffs.Slice(0, 16).CopyTo(this.Coeffs);
coeffs[..16].CopyTo(this.Coeffs);
}
// Simulate block coding, but only record statistics.
@ -93,23 +89,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Vp8StatsArray s = this.Stats[n].Stats[ctx];
if (this.Last < 0)
{
this.RecordStats(0, s, 0);
RecordStats(0, s, 0);
return 0;
}
while (n <= this.Last)
{
int v;
this.RecordStats(1, s, 0); // order of record doesn't matter
RecordStats(1, s, 0); // order of record doesn't matter
while ((v = this.Coeffs[n++]) == 0)
{
this.RecordStats(0, s, 1);
RecordStats(0, s, 1);
s = this.Stats[WebpConstants.Vp8EncBands[n]].Stats[0];
}
this.RecordStats(1, s, 1);
RecordStats(1, s, 1);
bool bit = (uint)(v + 1) > 2u;
if (this.RecordStats(bit ? 1 : 0, s, 2) == 0)
if (RecordStats(bit ? 1 : 0, s, 2) == 0)
{
// v = -1 or 1
s = this.Stats[WebpConstants.Vp8EncBands[n]].Stats[1];
@ -130,7 +126,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int mask = 2 << i;
if ((pattern & 1) != 0)
{
this.RecordStats((bits & mask) != 0 ? 1 : 0, s, 3 + i);
RecordStats((bits & mask) != 0 ? 1 : 0, s, 3 + i);
}
}
@ -140,7 +136,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (n < 16)
{
this.RecordStats(0, s, 0);
RecordStats(0, s, 0);
}
return 1;
@ -163,7 +159,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return LossyUtils.Vp8BitCost(0, (byte)p0);
}
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
Span<byte> ctxs = this.scratch.AsSpan(0, 16);
@ -211,7 +206,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return cost;
}
#endif
{
int v;
for (; n < this.Last; ++n)
@ -241,7 +236,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private static int LevelCost(Span<ushort> table, int level)
=> WebpLookupTables.Vp8LevelFixedCosts[level] + table[level > WebpConstants.MaxVariableLevel ? WebpConstants.MaxVariableLevel : level];
private int RecordStats(int bit, Vp8StatsArray statsArr, int idx)
private static int RecordStats(int bit, Vp8StatsArray statsArr, int idx)
{
// An overflow is inbound. Note we handle this at 0xfffe0000u instead of
// 0xffff0000u to make sure p + 1u does not overflow.

80
src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs

@ -5,10 +5,8 @@ using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -32,13 +30,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// ([3*a + b + 9*c + 3*d a + 3*b + 3*c + 9*d] [8 8]) / 16
public static void UpSample(Span<byte> topY, Span<byte> bottomY, Span<byte> topU, Span<byte> topV, Span<byte> curU, Span<byte> curV, Span<byte> topDst, Span<byte> bottomDst, int len, byte[] uvBuffer)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse41.IsSupported)
{
UpSampleSse41(topY, bottomY, topU, topV, curU, curV, topDst, bottomDst, len, uvBuffer);
}
else
#endif
{
UpSampleScalar(topY, bottomY, topU, topV, curU, curV, topDst, bottomDst, len);
}
@ -46,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private static void UpSampleScalar(Span<byte> topY, Span<byte> bottomY, Span<byte> topU, Span<byte> topV, Span<byte> curU, Span<byte> curV, Span<byte> topDst, Span<byte> bottomDst, int len)
{
int xStep = 3;
const int xStep = 3;
int lastPixelPair = (len - 1) >> 1;
uint tluv = LoadUv(topU[0], topV[0]); // top-left sample
uint luv = LoadUv(curU[0], curV[0]); // left-sample
@ -71,15 +67,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
uv0 = (diag12 + tluv) >> 1;
uint uv1 = (diag03 + tuv) >> 1;
int xMul2 = x * 2;
YuvToBgr(topY[xMul2 - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst.Slice((xMul2 - 1) * xStep));
YuvToBgr(topY[xMul2 - 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), topDst.Slice((xMul2 - 0) * xStep));
YuvToBgr(topY[xMul2 - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst[((xMul2 - 1) * xStep)..]);
YuvToBgr(topY[xMul2 - 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), topDst[((xMul2 - 0) * xStep)..]);
if (bottomY != default)
{
uv0 = (diag03 + luv) >> 1;
uv1 = (diag12 + uv) >> 1;
YuvToBgr(bottomY[xMul2 - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), bottomDst.Slice((xMul2 - 1) * xStep));
YuvToBgr(bottomY[xMul2 + 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), bottomDst.Slice((xMul2 + 0) * xStep));
YuvToBgr(bottomY[xMul2 - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), bottomDst[((xMul2 - 1) * xStep)..]);
YuvToBgr(bottomY[xMul2 + 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), bottomDst[((xMul2 + 0) * xStep)..]);
}
tluv = tuv;
@ -89,16 +85,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if ((len & 1) == 0)
{
uv0 = ((3 * tluv) + luv + 0x00020002u) >> 2;
YuvToBgr(topY[len - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst.Slice((len - 1) * xStep));
YuvToBgr(topY[len - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst[((len - 1) * xStep)..]);
if (bottomY != default)
{
uv0 = ((3 * luv) + tluv + 0x00020002u) >> 2;
YuvToBgr(bottomY[len - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), bottomDst.Slice((len - 1) * xStep));
YuvToBgr(bottomY[len - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), bottomDst[((len - 1) * xStep)..]);
}
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
// We compute (9*a + 3*b + 3*c + d + 8) / 16 as follows
// u = (9*a + 3*b + 3*c + d + 8) / 16
// = (a + (a + 3*b + 3*c + d) / 8 + 1) / 2
@ -118,7 +113,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
const int xStep = 3;
Array.Clear(uvBuffer);
Span<byte> ru = uvBuffer.AsSpan(15);
Span<byte> rv = ru.Slice(32);
Span<byte> rv = ru[32..];
// Treat the first pixel in regular way.
int uDiag = ((topU[0] + curU[0]) >> 1) + 1;
@ -140,7 +135,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
ref byte topVRef = ref MemoryMarshal.GetReference(topV);
ref byte curURef = ref MemoryMarshal.GetReference(curU);
ref byte curVRef = ref MemoryMarshal.GetReference(curV);
if (bottomY != null)
if (bottomY != default)
{
for (pos = 1, uvPos = 0; pos + 32 + 1 <= len; pos += 32, uvPos += 16)
{
@ -163,17 +158,17 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (len > 1)
{
int leftOver = ((len + 1) >> 1) - (pos >> 1);
Span<byte> tmpTopDst = ru.Slice(4 * 32);
Span<byte> tmpBottomDst = tmpTopDst.Slice(4 * 32);
Span<byte> tmpTop = tmpBottomDst.Slice(4 * 32);
Span<byte> tmpBottom = (bottomY == null) ? null : tmpTop.Slice(32);
UpSampleLastBlock(topU.Slice(uvPos), curU.Slice(uvPos), leftOver, ru);
UpSampleLastBlock(topV.Slice(uvPos), curV.Slice(uvPos), leftOver, rv);
topY.Slice(pos, len - pos).CopyTo(tmpTop);
Span<byte> tmpTopDst = ru[(4 * 32)..];
Span<byte> tmpBottomDst = tmpTopDst[(4 * 32)..];
Span<byte> tmpTop = tmpBottomDst[(4 * 32)..];
Span<byte> tmpBottom = (bottomY == default) ? null : tmpTop[32..];
UpSampleLastBlock(topU[uvPos..], curU[uvPos..], leftOver, ru);
UpSampleLastBlock(topV[uvPos..], curV[uvPos..], leftOver, rv);
topY[pos..len].CopyTo(tmpTop);
if (bottomY != default)
{
bottomY.Slice(pos, len - pos).CopyTo(tmpBottom);
bottomY[pos..len].CopyTo(tmpBottom);
ConvertYuvToBgrWithBottomYSse41(tmpTop, tmpBottom, tmpTopDst, tmpBottomDst, ru, rv, 0, xStep);
}
else
@ -181,10 +176,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
ConvertYuvToBgrSse41(tmpTop, tmpTopDst, ru, rv, 0, xStep);
}
tmpTopDst.Slice(0, (len - pos) * xStep).CopyTo(topDst.Slice(pos * xStep));
tmpTopDst[..((len - pos) * xStep)].CopyTo(topDst[(pos * xStep)..]);
if (bottomY != default)
{
tmpBottomDst.Slice(0, (len - pos) * xStep).CopyTo(bottomDst.Slice(pos * xStep));
tmpBottomDst[..((len - pos) * xStep)].CopyTo(bottomDst[(pos * xStep)..]);
}
}
}
@ -216,15 +211,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Pack the alternate pixels.
PackAndStore(a, b, diag1, diag2, output); // store top.
PackAndStore(c, d, diag2, diag1, output.Slice(2 * 32));
PackAndStore(c, d, diag2, diag1, output[(2 * 32)..]);
}
private static void UpSampleLastBlock(Span<byte> tb, Span<byte> bb, int numPixels, Span<byte> output)
{
Span<byte> r1 = stackalloc byte[17];
Span<byte> r2 = stackalloc byte[17];
tb.Slice(0, numPixels).CopyTo(r1);
bb.Slice(0, numPixels).CopyTo(r2);
tb[..numPixels].CopyTo(r1);
bb[..numPixels].CopyTo(r2);
// Replicate last byte.
int length = 17 - numPixels;
@ -263,7 +258,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Unsafe.As<byte, Vector128<byte>>(ref output0Ref) = t1;
Unsafe.As<byte, Vector128<byte>>(ref output1Ref) = t2;
}
#endif
/// <summary>
/// Converts the RGB values of the image to YUV.
@ -317,11 +311,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
AccumulateRgba(bgraRow0, bgraRow1, tmpRgbSpan, width);
}
ConvertRgbaToUv(tmpRgbSpan, u.Slice(uvRowIndex * uvWidth), v.Slice(uvRowIndex * uvWidth), uvWidth);
ConvertRgbaToUv(tmpRgbSpan, u[(uvRowIndex * uvWidth)..], v[(uvRowIndex * uvWidth)..], uvWidth);
uvRowIndex++;
ConvertRgbaToY(bgraRow0, y.Slice(rowIndex * width), width);
ConvertRgbaToY(bgraRow1, y.Slice((rowIndex + 1) * width), width);
ConvertRgbaToY(bgraRow0, y[(rowIndex * width)..], width);
ConvertRgbaToY(bgraRow1, y[((rowIndex + 1) * width)..], width);
}
// Extra last row.
@ -329,7 +323,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(rowIndex);
PixelOperations<TPixel>.Instance.ToBgra32(configuration, rowSpan, bgraRow0);
ConvertRgbaToY(bgraRow0, y.Slice(rowIndex * width), width);
ConvertRgbaToY(bgraRow0, y[(rowIndex * width)..], width);
if (!WebpCommonUtils.CheckNonOpaque(bgraRow0))
{
@ -341,7 +335,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
hasAlpha = true;
}
ConvertRgbaToUv(tmpRgbSpan, u.Slice(uvRowIndex * uvWidth), v.Slice(uvRowIndex * uvWidth), uvWidth);
ConvertRgbaToUv(tmpRgbSpan, u[(uvRowIndex * uvWidth)..], v[(uvRowIndex * uvWidth)..], uvWidth);
}
return hasAlpha;
@ -569,16 +563,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
bgr[0] = (byte)YuvToB(y, u);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(InliningOptions.ShortMethod)]
private static void ConvertYuvToBgrSse41(Span<byte> topY, Span<byte> topDst, Span<byte> ru, Span<byte> rv, int curX, int step) => YuvToBgrSse41(topY.Slice(curX), ru, rv, topDst.Slice(curX * step));
private static void ConvertYuvToBgrSse41(Span<byte> topY, Span<byte> topDst, Span<byte> ru, Span<byte> rv, int curX, int step) => YuvToBgrSse41(topY[curX..], ru, rv, topDst[(curX * step)..]);
[MethodImpl(InliningOptions.ShortMethod)]
private static void ConvertYuvToBgrWithBottomYSse41(Span<byte> topY, Span<byte> bottomY, Span<byte> topDst, Span<byte> bottomDst, Span<byte> ru, Span<byte> rv, int curX, int step)
{
YuvToBgrSse41(topY.Slice(curX), ru, rv, topDst.Slice(curX * step));
YuvToBgrSse41(bottomY.Slice(curX), ru.Slice(64), rv.Slice(64), bottomDst.Slice(curX * step));
YuvToBgrSse41(topY[curX..], ru, rv, topDst[(curX * step)..]);
YuvToBgrSse41(bottomY[curX..], ru[64..], rv[64..], bottomDst[(curX * step)..]);
}
private static void YuvToBgrSse41(Span<byte> y, Span<byte> u, Span<byte> v, Span<byte> dst)
@ -718,9 +710,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// R = (19077 * y + 26149 * v - 14234) >> 6
// G = (19077 * y - 6419 * u - 13320 * v + 8708) >> 6
// B = (19077 * y + 33050 * u - 17685) >> 6
Vector128<ushort> k19077 = Vector128.Create((ushort)19077);
Vector128<ushort> k26149 = Vector128.Create((ushort)26149);
Vector128<ushort> k14234 = Vector128.Create((ushort)14234);
var k19077 = Vector128.Create((ushort)19077);
var k26149 = Vector128.Create((ushort)26149);
var k14234 = Vector128.Create((ushort)14234);
Vector128<ushort> y1 = Sse2.MultiplyHigh(y0.AsUInt16(), k19077);
Vector128<ushort> r0 = Sse2.MultiplyHigh(v0.AsUInt16(), k26149);
@ -744,8 +736,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
b = Sse2.ShiftRightLogical(b2.AsInt16(), 6); // range: [0, 34238]
}
#endif
[MethodImpl(InliningOptions.ShortMethod)]
public static int YuvToB(int y, int u) => Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685);
@ -761,7 +751,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
private static byte Clip8(int v)
{
int yuvMask = (256 << 6) - 1;
const int yuvMask = (256 << 6) - 1;
return (byte)((v & ~yuvMask) == 0 ? v >> 6 : v < 0 ? 0 : 255);
}
}

28
src/ImageSharp/Formats/Webp/WebpCommonUtils.cs

@ -3,11 +3,9 @@
using System;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Webp
{
@ -23,7 +21,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// <returns>Returns true if alpha has non-0xff values.</returns>
public static unsafe bool CheckNonOpaque(Span<Bgra32> row)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
ReadOnlySpan<byte> rowBytes = MemoryMarshal.AsBytes(row);
@ -31,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
int length = (row.Length * 4) - 3;
fixed (byte* src = rowBytes)
{
Vector256<byte> alphaMaskVector256 = Vector256.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255);
var alphaMaskVector256 = Vector256.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255);
Vector256<byte> all0x80Vector256 = Vector256.Create((byte)0x80).AsByte();
for (; i + 128 <= length; i += 128)
@ -113,7 +110,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
}
else
#endif
{
for (int x = 0; x < row.Length; x++)
{
@ -127,10 +123,9 @@ namespace SixLabors.ImageSharp.Formats.Webp
return false;
}
#if SUPPORTS_RUNTIME_INTRINSICS
private static unsafe bool IsNoneOpaque64Bytes(byte* src, int i)
{
Vector128<byte> alphaMask = Vector128.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255);
var alphaMask = Vector128.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255);
Vector128<byte> a0 = Sse2.LoadVector128(src + i).AsByte();
Vector128<byte> a1 = Sse2.LoadVector128(src + i + 16).AsByte();
@ -145,17 +140,12 @@ namespace SixLabors.ImageSharp.Formats.Webp
Vector128<byte> d = Sse2.PackSignedSaturate(c0, c1).AsByte();
Vector128<byte> bits = Sse2.CompareEqual(d, Vector128.Create((byte)0x80).AsByte());
int mask = Sse2.MoveMask(bits);
if (mask != 0xFFFF)
{
return true;
}
return false;
return mask != 0xFFFF;
}
private static unsafe bool IsNoneOpaque32Bytes(byte* src, int i)
{
Vector128<byte> alphaMask = Vector128.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255);
var alphaMask = Vector128.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255);
Vector128<byte> a0 = Sse2.LoadVector128(src + i).AsByte();
Vector128<byte> a1 = Sse2.LoadVector128(src + i + 16).AsByte();
@ -165,13 +155,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
Vector128<byte> d = Sse2.PackSignedSaturate(c, c).AsByte();
Vector128<byte> bits = Sse2.CompareEqual(d, Vector128.Create((byte)0x80).AsByte());
int mask = Sse2.MoveMask(bits);
if (mask != 0xFFFF)
{
return true;
}
return false;
return mask != 0xFFFF;
}
#endif
}
}

4
src/ImageSharp/IO/BufferedReadStream.cs

@ -175,7 +175,6 @@ namespace SixLabors.ImageSharp.IO
return this.ReadToBufferViaCopyFast(buffer, offset, count);
}
#if SUPPORTS_SPAN_STREAM
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int Read(Span<byte> buffer)
@ -196,7 +195,6 @@ namespace SixLabors.ImageSharp.IO
return this.ReadToBufferViaCopyFast(buffer);
}
#endif
/// <inheritdoc/>
public override void Flush()
@ -354,7 +352,7 @@ namespace SixLabors.ImageSharp.IO
int i;
do
{
i = baseStream.Read(buffer.Slice(n, count - n));
i = baseStream.Read(buffer[n..count]);
n += i;
}
while (n < count && i > 0);

4
src/ImageSharp/IO/ChunkedMemoryStream.cs

@ -236,11 +236,9 @@ namespace SixLabors.ImageSharp.IO
return this.ReadImpl(buffer.AsSpan(offset, count));
}
#if SUPPORTS_SPAN_STREAM
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int Read(Span<byte> buffer) => this.ReadImpl(buffer);
#endif
private int ReadImpl(Span<byte> buffer)
{
@ -352,11 +350,9 @@ namespace SixLabors.ImageSharp.IO
this.WriteImpl(buffer.AsSpan(offset, count));
}
#if SUPPORTS_SPAN_STREAM
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Write(ReadOnlySpan<byte> buffer) => this.WriteImpl(buffer);
#endif
private void WriteImpl(ReadOnlySpan<byte> buffer)
{

16
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs

@ -7,7 +7,6 @@ using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using SixLabors.ImageSharp.Memory.Internals;
namespace SixLabors.ImageSharp.Memory
@ -129,7 +128,7 @@ namespace SixLabors.ImageSharp.Memory
if (bufferCount > 0)
{
buffers[buffers.Length - 1] = allocator.Allocate<T>(sizeOfLastBuffer, options);
buffers[^1] = allocator.Allocate<T>(sizeOfLastBuffer, options);
}
return new Owned(buffers, bufferLength, totalLengthInElements, true);
@ -214,12 +213,12 @@ namespace SixLabors.ImageSharp.Memory
}
}
if (source.Length > 0 && source[source.Length - 1].Length > bufferLength)
if (source.Length > 0 && source[^1].Length > bufferLength)
{
throw new InvalidMemoryOperationException("Wrap: the last buffer is too large!");
}
long totalLength = bufferLength > 0 ? ((long)bufferLength * (source.Length - 1)) + source[source.Length - 1].Length : 0;
long totalLength = bufferLength > 0 ? ((long)bufferLength * (source.Length - 1)) + source[^1].Length : 0;
return new Consumed(source, bufferLength, totalLength);
}
@ -235,12 +234,12 @@ namespace SixLabors.ImageSharp.Memory
}
}
if (source.Length > 0 && source[source.Length - 1].Memory.Length > bufferLength)
if (source.Length > 0 && source[^1].Memory.Length > bufferLength)
{
throw new InvalidMemoryOperationException("Wrap: the last buffer is too large!");
}
long totalLength = bufferLength > 0 ? ((long)bufferLength * (source.Length - 1)) + source[source.Length - 1].Memory.Length : 0;
long totalLength = bufferLength > 0 ? ((long)bufferLength * (source.Length - 1)) + source[^1].Memory.Length : 0;
return new Owned(source, bufferLength, totalLength, false);
}
@ -252,15 +251,10 @@ namespace SixLabors.ImageSharp.Memory
{
case SpanCacheMode.SingleArray:
{
#if SUPPORTS_CREATESPAN
ref byte b0 = ref MemoryMarshal.GetReference<byte>(this.memoryGroupSpanCache.SingleArray);
ref T e0 = ref Unsafe.As<byte, T>(ref b0);
e0 = ref Unsafe.Add(ref e0, y * width);
return MemoryMarshal.CreateSpan(ref e0, width);
#else
return MemoryMarshal.Cast<byte, T>(this.memoryGroupSpanCache.SingleArray).Slice(y * width, width);
#endif
}
case SpanCacheMode.SinglePointer:

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

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.PixelFormats
internal partial class PixelOperations : PixelOperations<Rgb24>
{
private static readonly Lazy<PixelTypeInfo> LazyInfo =
new Lazy<PixelTypeInfo>(() => PixelTypeInfo.Create<Rgb24>(PixelAlphaRepresentation.None), true);
new(() => PixelTypeInfo.Create<Rgb24>(PixelAlphaRepresentation.None), true);
/// <inheritdoc />
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value;
@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.PixelFormats
int count = redChannel.Length;
GuardPackFromRgbPlanes(greenChannel, blueChannel, destination, count);
SimdUtils.PackFromRgbPlanes(configuration, redChannel, greenChannel, blueChannel, destination);
SimdUtils.PackFromRgbPlanes(redChannel, greenChannel, blueChannel, destination);
}
}
}

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

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.PixelFormats
internal partial class PixelOperations : PixelOperations<Rgba32>
{
private static readonly Lazy<PixelTypeInfo> LazyInfo =
new Lazy<PixelTypeInfo>(() => PixelTypeInfo.Create<Rgba32>(PixelAlphaRepresentation.Unassociated), true);
new(() => PixelTypeInfo.Create<Rgba32>(PixelAlphaRepresentation.Unassociated), true);
/// <inheritdoc />
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value;
@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.PixelFormats
{
Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationVectors, nameof(destinationVectors));
destinationVectors = destinationVectors.Slice(0, sourcePixels.Length);
destinationVectors = destinationVectors[..sourcePixels.Length];
SimdUtils.ByteToNormalizedFloat(
MemoryMarshal.Cast<Rgba32, byte>(sourcePixels),
MemoryMarshal.Cast<Vector4, float>(destinationVectors));
@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.PixelFormats
{
Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationPixels, nameof(destinationPixels));
destinationPixels = destinationPixels.Slice(0, sourceVectors.Length);
destinationPixels = destinationPixels[..sourceVectors.Length];
Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers);
SimdUtils.NormalizedFloatToByteSaturate(
MemoryMarshal.Cast<Vector4, float>(sourceVectors),
@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.PixelFormats
int count = redChannel.Length;
GuardPackFromRgbPlanes(greenChannel, blueChannel, destination, count);
SimdUtils.PackFromRgbPlanes(configuration, redChannel, greenChannel, blueChannel, destination);
SimdUtils.PackFromRgbPlanes(redChannel, greenChannel, blueChannel, destination);
}
}
}

8
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs

@ -4,11 +4,9 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
@ -56,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public Span<float> Values
{
[MethodImpl(InliningOptions.ShortMethod)]
get => new Span<float>(this.bufferPtr, this.Length);
get => new(this.bufferPtr, this.Length);
}
/// <summary>
@ -71,7 +69,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
[MethodImpl(InliningOptions.ShortMethod)]
public Vector4 ConvolveCore(ref Vector4 rowStartRef)
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && Fma.IsSupported)
{
float* bufferStart = this.bufferPtr;
@ -141,7 +138,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return *(Vector4*)&result128;
}
else
#endif
{
// Destination color components
Vector4 result = Vector4.Zero;
@ -167,7 +163,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal ResizeKernel AlterLeftValue(int left)
=> new ResizeKernel(left, this.bufferPtr, this.Length);
=> new(left, this.bufferPtr, this.Length);
internal void Fill(Span<double> values)
{

10
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs

@ -5,10 +5,8 @@ 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;
#endif
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
@ -169,7 +167,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
Unsafe.As<float, Vector<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 7))) = v3;
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void UseVector256_Avx2_Variant1()
{
@ -319,7 +316,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
fixed (Block8x8F* ss = &this.block)
{
var s = (float*)ss;
float* s = (float*)ss;
Vector256<float> v0 = Avx.LoadVector256(s);
Vector256<float> v1 = Avx.LoadVector256(s + 8);
Vector256<float> v2 = Avx.LoadVector256(s + (8 * 2));
@ -345,8 +342,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
public void UseVector256_Avx2_Variant3_sbyte()
{
int stride = Width * 4;
var d = (sbyte*)this.bufferPtr;
var s = (sbyte*)this.blockPtr;
sbyte* d = (sbyte*)this.bufferPtr;
sbyte* s = (sbyte*)this.blockPtr;
Vector256<sbyte> v0 = Avx.LoadVector256(s);
Vector256<sbyte> v1 = Avx.LoadVector256(s + 32);
@ -366,7 +363,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
Avx.Store(d + (stride * 6), v2);
Avx.Store(d + (stride * 7), v3);
}
#endif
// *** RESULTS 02/2020 ***
// BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363

4
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs

@ -5,10 +5,8 @@ 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;
#endif
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
@ -136,7 +134,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
row3 = SimdUtils.FastRound(row3);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void Sse41_V1()
{
@ -491,6 +488,5 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
Sse.StoreAligned(p6, v6);
Sse.StoreAligned(p7, v7);
}
#endif
}
}

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

@ -30,7 +30,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromCmykVector(8).ConvertToRgbInplace(values);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void SimdVectorAvx()
{
@ -38,6 +37,5 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromCmykAvx(8).ConvertToRgbInplace(values);
}
#endif
}
}

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

@ -22,7 +22,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromGrayscaleScalar(8).ConvertToRgbInplace(values);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void SimdVectorAvx()
{
@ -30,6 +29,5 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromGrayscaleAvx(8).ConvertToRgbInplace(values);
}
#endif
}
}

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

@ -30,7 +30,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromRgbVector(8).ConvertToRgbInplace(values);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void SimdVectorAvx()
{
@ -38,6 +37,5 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromRgbAvx(8).ConvertToRgbInplace(values);
}
#endif
}
}

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

@ -30,7 +30,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromYCbCrVector(8).ConvertToRgbInplace(values);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void SimdVectorAvx()
{
@ -38,6 +37,5 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromYCbCrAvx(8).ConvertToRgbInplace(values);
}
#endif
}
}

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

@ -30,7 +30,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromYccKVector(8).ConvertToRgbInplace(values);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void SimdVectorAvx2()
{
@ -38,6 +37,5 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromYccKAvx(8).ConvertToRgbInplace(values);
}
#endif
}
}

13
tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs

@ -6,10 +6,8 @@ using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -80,15 +78,6 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
SimdUtils.FallbackIntrinsics128.NormalizedFloatToByteSaturate(sBytes, dFloats);
}
[Benchmark]
public void BasicIntrinsics256()
{
Span<float> sBytes = MemoryMarshal.Cast<Vector4, float>(this.source.GetSpan());
Span<byte> dFloats = MemoryMarshal.Cast<Rgba32, byte>(this.destination.GetSpan());
SimdUtils.BasicIntrinsics256.NormalizedFloatToByteSaturate(sBytes, dFloats);
}
[Benchmark]
public void ExtendedIntrinsic()
{
@ -98,7 +87,6 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
SimdUtils.ExtendedIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void UseHwIntrinsics()
{
@ -161,7 +149,6 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
vf = Avx.Multiply(scale, vf);
return Avx.ConvertToVector256Int32(vf);
}
#endif
// *** RESULTS 2020 March: ***
// Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores

19
tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs

@ -26,21 +26,10 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
[Benchmark]
public void PixelOperations_Base()
{
new PixelOperations<Rgba32>().ToVector4(
=> new PixelOperations<Rgba32>().ToVector4(
this.Configuration,
this.source.GetSpan(),
this.destination.GetSpan());
}
[Benchmark(Baseline = true)]
public void BasicIntrinsics256()
{
Span<byte> sBytes = MemoryMarshal.Cast<Rgba32, byte>(this.source.GetSpan());
Span<float> dFloats = MemoryMarshal.Cast<Vector4, float>(this.destination.GetSpan());
SimdUtils.BasicIntrinsics256.ByteToNormalizedFloat(sBytes, dFloats);
}
[Benchmark]
public void ExtendedIntrinsics()
@ -51,7 +40,6 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
SimdUtils.ExtendedIntrinsics.ByteToNormalizedFloat(sBytes, dFloats);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void HwIntrinsics()
{
@ -60,7 +48,6 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
SimdUtils.HwIntrinsics.ByteToNormalizedFloat(sBytes, dFloats);
}
#endif
// [Benchmark]
public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_2Loops()
@ -96,8 +83,8 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
{
ref Vector<float> dRef = ref Unsafe.Add(ref destBase, i);
Vector<int> du = Vector.AsVectorInt32(dRef);
Vector<float> v = Vector.ConvertToSingle(du);
var du = Vector.AsVectorInt32(dRef);
var v = Vector.ConvertToSingle(du);
v *= scale;
dRef = v;

10
tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs

@ -1,9 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86;
#endif
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
@ -58,26 +56,24 @@ namespace SixLabors.ImageSharp.Benchmarks
{
public HwIntrinsics_SSE_AVX()
{
this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core31)
this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core60)
.WithEnvironmentVariables(
new EnvironmentVariable(EnableHWIntrinsic, Off),
new EnvironmentVariable(FeatureSIMD, Off))
.WithId("1. No HwIntrinsics").AsBaseline());
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse.IsSupported)
{
this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core31)
this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core60)
.WithEnvironmentVariables(new EnvironmentVariable(EnableAVX, Off))
.WithId("2. SSE"));
}
if (Avx.IsSupported)
{
this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core31)
this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core60)
.WithId("3. AVX"));
}
#endif
}
}
}

4
tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs

@ -4,10 +4,8 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats;
@ -200,7 +198,6 @@ namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark(Baseline = true)]
public void Rgba32_Avx2_Float()
{
@ -261,7 +258,6 @@ namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion
Span<Rgba32> rgb = this.rgbaBuf;
SimdUtils.HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref r, ref g, ref b, ref rgb);
}
#endif
#pragma warning disable SA1132
private struct Byte8

93
tests/ImageSharp.Tests/Common/SimdUtilsTests.cs

@ -5,9 +5,7 @@ using System;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
@ -43,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Common
private static Vector<float> CreateExactTestVector1()
{
var data = new float[Vector<float>.Count];
float[] data = new float[Vector<float>.Count];
data[0] = 0.1f;
data[1] = 0.4f;
@ -113,53 +111,6 @@ namespace SixLabors.ImageSharp.Tests.Common
return false;
}
[Theory]
[InlineData(1, 0)]
[InlineData(1, 8)]
[InlineData(2, 16)]
[InlineData(3, 128)]
public void BasicIntrinsics256_BulkConvertNormalizedFloatToByte_WithRoundedData(int seed, int count)
{
if (this.SkipOnNonAvx2())
{
return;
}
float[] orig = new Random(seed).GenerateRandomRoundedFloatArray(count, 0, 256);
float[] normalized = orig.Select(f => f / 255f).ToArray();
var dest = new byte[count];
SimdUtils.BasicIntrinsics256.BulkConvertNormalizedFloatToByte(normalized, dest);
byte[] expected = orig.Select(f => (byte)f).ToArray();
Assert.Equal(expected, dest);
}
[Theory]
[InlineData(1, 0)]
[InlineData(1, 8)]
[InlineData(2, 16)]
[InlineData(3, 128)]
public void BasicIntrinsics256_BulkConvertNormalizedFloatToByte_WithNonRoundedData(int seed, int count)
{
if (this.SkipOnNonAvx2())
{
return;
}
float[] source = new Random(seed).GenerateRandomFloatArray(count, 0, 1f);
byte[] dest = new byte[count];
SimdUtils.BasicIntrinsics256.BulkConvertNormalizedFloatToByte(source, dest);
byte[] expected = source.Select(f => (byte)Math.Round(f * 255f)).ToArray();
Assert.Equal(expected, dest);
}
public static readonly TheoryData<int> ArraySizesDivisibleBy8 = new() { 0, 8, 16, 1024 };
public static readonly TheoryData<int> ArraySizesDivisibleBy4 = new() { 0, 4, 8, 28, 1020 };
public static readonly TheoryData<int> ArraySizesDivisibleBy3 = new() { 0, 3, 9, 36, 957 };
@ -173,27 +124,12 @@ namespace SixLabors.ImageSharp.Tests.Common
count,
(s, d) => SimdUtils.FallbackIntrinsics128.ByteToNormalizedFloat(s.Span, d.Span));
[Theory]
[MemberData(nameof(ArraySizesDivisibleBy8))]
public void BasicIntrinsics256_BulkConvertByteToNormalizedFloat(int count)
{
if (this.SkipOnNonAvx2())
{
return;
}
TestImpl_BulkConvertByteToNormalizedFloat(
count,
(s, d) => SimdUtils.BasicIntrinsics256.ByteToNormalizedFloat(s.Span, d.Span));
}
[Theory]
[MemberData(nameof(ArraySizesDivisibleBy32))]
public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat(int count) => TestImpl_BulkConvertByteToNormalizedFloat(
count,
(s, d) => SimdUtils.ExtendedIntrinsics.ByteToNormalizedFloat(s.Span, d.Span));
#if SUPPORTS_RUNTIME_INTRINSICS
[Theory]
[MemberData(nameof(ArraySizesDivisibleBy32))]
public void HwIntrinsics_BulkConvertByteToNormalizedFloat(int count)
@ -212,7 +148,6 @@ namespace SixLabors.ImageSharp.Tests.Common
count,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE41);
}
#endif
[Theory]
[MemberData(nameof(ArbitraryArraySizes))]
@ -239,18 +174,6 @@ namespace SixLabors.ImageSharp.Tests.Common
count,
(s, d) => SimdUtils.FallbackIntrinsics128.NormalizedFloatToByteSaturate(s.Span, d.Span));
[Theory]
[MemberData(nameof(ArraySizesDivisibleBy8))]
public void BasicIntrinsics256_BulkConvertNormalizedFloatToByteClampOverflows(int count)
{
if (this.SkipOnNonAvx2())
{
return;
}
TestImpl_BulkConvertNormalizedFloatToByteClampOverflows(count, (s, d) => SimdUtils.BasicIntrinsics256.NormalizedFloatToByteSaturate(s.Span, d.Span));
}
[Theory]
[MemberData(nameof(ArraySizesDivisibleBy32))]
public void ExtendedIntrinsics_BulkConvertNormalizedFloatToByteClampOverflows(int count) => TestImpl_BulkConvertNormalizedFloatToByteClampOverflows(
@ -278,8 +201,6 @@ namespace SixLabors.ImageSharp.Tests.Common
Assert.Equal(expected2, actual2);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Theory]
[MemberData(nameof(ArraySizesDivisibleBy32))]
public void HwIntrinsics_BulkConvertNormalizedFloatToByteClampOverflows(int count)
@ -299,8 +220,6 @@ namespace SixLabors.ImageSharp.Tests.Common
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2);
}
#endif
[Theory]
[MemberData(nameof(ArbitraryArraySizes))]
public void BulkConvertNormalizedFloatToByteClampOverflows(int count)
@ -325,16 +244,15 @@ namespace SixLabors.ImageSharp.Tests.Common
public void PackFromRgbPlanes_Rgb24(int count) => TestPackFromRgbPlanes<Rgb24>(
count,
(r, g, b, actual) =>
SimdUtils.PackFromRgbPlanes(Configuration.Default, r, g, b, actual));
SimdUtils.PackFromRgbPlanes(r, g, b, actual));
[Theory]
[MemberData(nameof(ArbitraryArraySizes))]
public void PackFromRgbPlanes_Rgba32(int count) => TestPackFromRgbPlanes<Rgba32>(
count,
(r, g, b, actual) =>
SimdUtils.PackFromRgbPlanes(Configuration.Default, r, g, b, actual));
SimdUtils.PackFromRgbPlanes(r, g, b, actual));
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void PackFromRgbPlanesAvx2Reduce_Rgb24()
{
@ -403,7 +321,6 @@ namespace SixLabors.ImageSharp.Tests.Common
Assert.Equal(0, bb.Length);
Assert.Equal(0, dd.Length);
}
#endif
internal static void TestPackFromRgbPlanes<TPixel>(int count, Action<byte[], byte[], byte[], TPixel[]> packMethod)
where TPixel : unmanaged, IPixel<TPixel>
@ -422,7 +339,7 @@ namespace SixLabors.ImageSharp.Tests.Common
var actual = new TPixel[count + 3]; // padding for Rgb24 AVX2
packMethod(r, g, b, actual);
Assert.True(expected.AsSpan().SequenceEqual(actual.AsSpan().Slice(0, count)));
Assert.True(expected.AsSpan().SequenceEqual(actual.AsSpan()[..count]));
}
private static void TestImpl_BulkConvertNormalizedFloatToByteClampOverflows(
@ -434,7 +351,7 @@ namespace SixLabors.ImageSharp.Tests.Common
seed = seed > 0 ? seed : count;
float[] source = new Random(seed).GenerateRandomFloatArray(count, -0.2f, 1.2f);
byte[] expected = source.Select(NormalizedFloatToByte).ToArray();
var actual = new byte[count];
byte[] actual = new byte[count];
convert(source, actual);

4
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

@ -4,9 +4,7 @@
// Uncomment this to turn unit tests into benchmarks:
// #define BENCHMARKING
using System;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.ImageSharp.Tests.TestUtilities;
@ -424,7 +422,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
static void RunTest(string serializedEqualsTo)
{
int equalsTo = FeatureTestRunner.Deserialize<int>(serializedEqualsTo);
int offValue = 0;
const int offValue = 0;
// Fill matrix with valid value
Block8x8F block = default;

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

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.ColorSpaces.Conversion;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
@ -24,11 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
private const int TestBufferLength = 40;
#if SUPPORTS_RUNTIME_INTRINSICS
private static readonly HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX;
#else
private static readonly HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll;
#endif
private const HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX;
private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new(epsilon: Precision);
@ -37,24 +32,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public static readonly TheoryData<int> Seeds = new() { 1, 2, 3 };
public JpegColorConverterTests(ITestOutputHelper output)
{
this.Output = output;
}
=> this.Output = output;
private ITestOutputHelper Output { get; }
[Fact]
public void GetConverterThrowsExceptionOnInvalidColorSpace()
{
Assert.Throws<Exception>(() => JpegColorConverterBase.GetConverter(JpegColorSpace.Undefined, 8));
}
=> Assert.Throws<Exception>(() => JpegColorConverterBase.GetConverter(JpegColorSpace.Undefined, 8));
[Fact]
public void GetConverterThrowsExceptionOnInvalidPrecision()
{
// Valid precisions: 8 & 12 bit
Assert.Throws<Exception>(() => JpegColorConverterBase.GetConverter(JpegColorSpace.YCbCr, 9));
}
=> Assert.Throws<Exception>(() => JpegColorConverterBase.GetConverter(JpegColorSpace.YCbCr, 9));
[Theory]
[InlineData(JpegColorSpace.Grayscale, 8)]
@ -242,7 +232,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
FeatureTestRunner.Deserialize<int>(arg));
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Theory]
[MemberData(nameof(Seeds))]
public void FromYCbCrAvx2(int seed) =>
@ -267,7 +256,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[MemberData(nameof(Seeds))]
public void FromYccKAvx2(int seed) =>
this.TestConverter(new JpegColorConverterBase.FromYccKAvx(8), 4, seed);
#endif
private void TestConverter(
JpegColorConverterBase converter,

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

@ -1,22 +1,10 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
// Licensed under the Six Labors Split License.
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
using Xunit;
using Xunit.Abstractions;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Jpg

30
tests/ImageSharp.Tests/Formats/Jpg/RgbToYCbCrConverterTests.cs

@ -4,10 +4,8 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder;
@ -22,16 +20,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public class RgbToYCbCrConverterTests
{
public RgbToYCbCrConverterTests(ITestOutputHelper output)
{
this.Output = output;
}
=> this.Output = output;
private ITestOutputHelper Output { get; }
[Fact]
public void TestConverterLut444()
{
int dataSize = 8 * 8;
const int dataSize = 8 * 8;
Rgb24[] data = CreateTestData(dataSize);
var target = RgbToYCbCrConverterLut.Create();
@ -53,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
return;
}
int dataSize = 8 * 8;
const int dataSize = 8 * 8;
Rgb24[] data = CreateTestData(dataSize);
Block8x8F y = default;
@ -68,7 +64,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Fact]
public void TestConverterLut420()
{
int dataSize = 16 * 16;
const int dataSize = 16 * 16;
Span<Rgb24> data = CreateTestData(dataSize).AsSpan();
var target = RgbToYCbCrConverterLut.Create();
@ -77,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var cr = default(Block8x8F);
target.Convert420(data, ref yBlocks[0], ref yBlocks[1], ref cb, ref cr, 0);
target.Convert420(data.Slice(16 * 8), ref yBlocks[2], ref yBlocks[3], ref cb, ref cr, 1);
target.Convert420(data[(16 * 8)..], ref yBlocks[2], ref yBlocks[3], ref cb, ref cr, 1);
Verify420(data, yBlocks, ref cb, ref cr, new ApproximateFloatComparer(1F));
}
@ -91,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
return;
}
int dataSize = 16 * 16;
const int dataSize = 16 * 16;
Span<Rgb24> data = CreateTestData(dataSize).AsSpan();
var yBlocks = new Block8x8F[4];
@ -99,12 +95,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var cr = default(Block8x8F);
RgbToYCbCrConverterVectorized.Convert420(data, ref yBlocks[0], ref yBlocks[1], ref cb, ref cr, 0);
RgbToYCbCrConverterVectorized.Convert420(data.Slice(16 * 8), ref yBlocks[2], ref yBlocks[3], ref cb, ref cr, 1);
RgbToYCbCrConverterVectorized.Convert420(data[(16 * 8)..], ref yBlocks[2], ref yBlocks[3], ref cb, ref cr, 1);
Verify420(data, yBlocks, ref cb, ref cr, new ApproximateFloatComparer(1F));
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Theory]
[InlineData(1)]
[InlineData(2)]
@ -140,7 +135,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
return 0.25f * (data[upIdx] + data[upIdx + 1] + data[lowIdx] + data[lowIdx + 1]);
}
}
#endif
private static void Verify444(
ReadOnlySpan<Rgb24> data,
@ -180,17 +174,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
VerifyBlock(ref yResult[0], ref trueBlock, comparer);
// top right
Copy8x8(data.Slice(8), tempData);
Copy8x8(data[8..], tempData);
RgbToYCbCr(tempData, ref trueBlock, ref cbTrue[1], ref crTrue[1]);
VerifyBlock(ref yResult[1], ref trueBlock, comparer);
// bottom left
Copy8x8(data.Slice(8 * 16), tempData);
Copy8x8(data[(8 * 16)..], tempData);
RgbToYCbCr(tempData, ref trueBlock, ref cbTrue[2], ref crTrue[2]);
VerifyBlock(ref yResult[2], ref trueBlock, comparer);
// bottom right
Copy8x8(data.Slice((8 * 16) + 8), tempData);
Copy8x8(data[((8 * 16) + 8)..], tempData);
RgbToYCbCr(tempData, ref trueBlock, ref cbTrue[3], ref crTrue[3]);
VerifyBlock(ref yResult[3], ref trueBlock, comparer);
@ -207,7 +201,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
for (int i = 0; i < 8; i++)
{
source.Slice(i * 16, 8).CopyTo(dest.Slice(i * 8));
source.Slice(i * 16, 8).CopyTo(dest[(i * 8)..]);
}
}
@ -259,7 +253,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var data = new Rgb24[size];
var r = new Random();
var random = new byte[3];
byte[] random = new byte[3];
for (int i = 0; i < data.Length; i++)
{
r.NextBytes(random);

2
tests/ImageSharp.Tests/Formats/Png/Adler32Tests.cs

@ -51,7 +51,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
return data;
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void RunCalculateAdlerTest_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCalculateAdlerTest, HwIntrinsics.AllowAll);
@ -69,6 +68,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
CalculateAdlerAndCompareToReference(testData[i]);
}
}
#endif
}
}

2
tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs

@ -147,7 +147,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
[Fact]
public void PaethFilter_Works() => RunPaethFilterTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void AverageFilter_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAverageFilterTest, HwIntrinsics.AllowAll);
@ -174,6 +173,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
[Fact]
public void PaethFilter_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.DisableHWIntrinsic);
#endif
}
}

35
tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs

@ -30,7 +30,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation
0b10010000
};
private static readonly Rgba32[][] BilevelResult4X4 = {
private static readonly Rgba32[][] BilevelResult4X4 =
{
new[] { Bit0, Bit1, Bit0, Bit1 },
new[] { Bit1, Bit1, Bit1, Bit1 },
new[] { Bit0, Bit1, Bit1, Bit1 },
@ -154,41 +155,25 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation
[MemberData(nameof(Grayscale4_Data))]
[MemberData(nameof(Grayscale8_Data))]
public void Decode_WritesPixelData(byte[] inputData, ushort bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult)
{
AssertDecode(expectedResult, pixels =>
{
new BlackIsZeroTiffColor<Rgba32>(new TiffBitsPerSample(bitsPerSample, 0, 0)).Decode(inputData, pixels, left, top, width, height);
});
}
=> AssertDecode(
expectedResult,
pixels => new BlackIsZeroTiffColor<Rgba32>(new TiffBitsPerSample(bitsPerSample, 0, 0)).Decode(inputData, pixels, left, top, width, height));
[Theory]
[MemberData(nameof(BilevelData))]
public void Decode_WritesPixelData_Bilevel(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult)
{
AssertDecode(expectedResult, pixels =>
{
new BlackIsZero1TiffColor<Rgba32>().Decode(inputData, pixels, left, top, width, height);
});
}
=> AssertDecode(expectedResult, pixels => new BlackIsZero1TiffColor<Rgba32>().Decode(inputData, pixels, left, top, width, height));
[Theory]
[MemberData(nameof(Grayscale4_Data))]
public void Decode_WritesPixelData_4Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult)
{
AssertDecode(expectedResult, pixels =>
{
new BlackIsZero4TiffColor<Rgba32>().Decode(inputData, pixels, left, top, width, height);
});
}
=> AssertDecode(
expectedResult,
pixels => new BlackIsZero4TiffColor<Rgba32>().Decode(inputData, pixels, left, top, width, height));
[Theory]
[MemberData(nameof(Grayscale8_Data))]
public void Decode_WritesPixelData_8Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult)
{
AssertDecode(expectedResult, pixels =>
{
new BlackIsZero8TiffColor<Rgba32>(Configuration.Default).Decode(inputData, pixels, left, top, width, height);
});
}
=> AssertDecode(expectedResult, pixels => new BlackIsZero8TiffColor<Rgba32>(Configuration.Default).Decode(inputData, pixels, left, top, width, height));
}
}

3
tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs

@ -68,7 +68,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void CollectColorRedTransforms_Works() => RunCollectColorRedTransformsTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void CollectColorBlueTransforms_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.AllowAll);
@ -86,7 +85,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void CollectColorRedTransforms_WithoutAvx2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.DisableAVX2);
#endif
}
}

16
tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
{
int[] x = { 3, 5, 2, 5, 3, 1, 2, 2, 3, 3, 1, 2, 1, 2, 1, 1, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 1, 1, 0, 0, 2, 1, 1, 0, 3, 1, 2, 3, 2, 3 };
int[] y = { 11, 12, 8, 3, 4, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 1, 1, 2, 4, 6, 4 };
float expected = 884.7585f;
const float expected = 884.7585f;
float actual = LosslessUtils.CombinedShannonEntropy(x, y);
@ -147,9 +147,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
{
// arrange
uint[] topData = { 4278258949, 4278258949 };
uint left = 4294839812;
const uint left = 4294839812;
short[] scratch = new short[8];
uint expectedResult = 4294839812;
const uint expectedResult = 4294839812;
// act
unsafe
@ -168,8 +168,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
{
// arrange
uint[] topData = { 4294844413, 4294779388 };
uint left = 4294844413;
uint expectedResult = 4294779388;
const uint left = 4294844413;
const uint expectedResult = 4294779388;
// act
unsafe
@ -188,8 +188,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
{
// arrange
uint[] topData = { 4278193922, 4278193666 };
uint left = 4278193410;
uint expectedResult = 4278193154;
const uint left = 4278193410;
const uint expectedResult = 4278193154;
// act
unsafe
@ -228,7 +228,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void TransformColorInverse_Works() => RunTransformColorInverseTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void CombinedShannonEntropy_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCombinedShannonEntropyTest, HwIntrinsics.AllowAll);
@ -288,6 +287,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void TransformColorInverse_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.DisableAVX2);
#endif
}
}

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

@ -301,7 +301,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void HadamardTransform_Works() => RunHadamardTransformTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void TransformTwo_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformTwoTest, HwIntrinsics.AllowAll);
@ -352,6 +351,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void HadamardTransform_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunHadamardTransformTest, HwIntrinsics.DisableHWIntrinsic);
#endif
}
}

8
tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs

@ -6,9 +6,7 @@ using System.IO;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Formats.Webp.Lossless;
using SixLabors.ImageSharp.PixelFormats;
#if SUPPORTS_RUNTIME_INTRINSICS
using SixLabors.ImageSharp.Tests.TestUtilities;
#endif
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Webp
@ -24,7 +22,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public static void ColorSpaceTransform_WithPeakImage_ProducesExpectedData()
=> RunColorSpaceTransformTestWithPeakImage();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void ColorSpaceTransform_WithPeakImage_WithHardwareIntrinsics_Works()
=> FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithPeakImage_ProducesExpectedData, HwIntrinsics.AllowAll);
@ -44,7 +41,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void ColorSpaceTransform_WithBikeImage_WithoutAvx2_Works()
=> FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithBikeImage_ProducesExpectedData, HwIntrinsics.DisableAVX2);
#endif
// Test image: Input\Webp\peak.png
private static void RunColorSpaceTransformTestWithPeakImage()
@ -91,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
using var image = Image.Load<Rgba32>(imgBytes);
uint[] bgra = ToBgra(image);
int colorTransformBits = 3;
const int colorTransformBits = 3;
int transformWidth = LosslessUtils.SubSampleSize(image.Width, colorTransformBits);
int transformHeight = LosslessUtils.SubSampleSize(image.Height, colorTransformBits);
uint[] transformData = new uint[transformWidth * transformHeight];
@ -122,7 +118,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
using var image = Image.Load<Rgba32>(imgBytes, new WebpDecoder());
uint[] bgra = ToBgra(image);
int colorTransformBits = 4;
const int colorTransformBits = 4;
int transformWidth = LosslessUtils.SubSampleSize(image.Width, colorTransformBits);
int transformHeight = LosslessUtils.SubSampleSize(image.Height, colorTransformBits);
uint[] transformData = new uint[transformWidth * transformHeight];

2
tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs

@ -42,7 +42,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void QuantizeBlock_Works() => RunQuantizeBlockTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void QuantizeBlock_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.AllowAll);
@ -51,6 +50,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void QuantizeBlock_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.DisableAVX2);
#endif
}
}

2
tests/ImageSharp.Tests/Formats/WebP/Vp8EncodingTests.cs

@ -138,7 +138,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void TwoInverseTransform_Works() => RunTwoInverseTransformTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void FTransform2_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunFTransform2Test, HwIntrinsics.AllowAll);
@ -162,6 +161,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void TwoInverseTransform_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTwoInverseTransformTest, HwIntrinsics.DisableHWIntrinsic);
#endif
}
}

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

@ -215,12 +215,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
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
}
}

2
tests/ImageSharp.Tests/Formats/WebP/Vp8LHistogramTests.cs

@ -99,12 +99,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void AddVector_Works() => RunAddVectorTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void AddVector_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddVectorTest, HwIntrinsics.AllowAll);
[Fact]
public void AddVector_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddVectorTest, HwIntrinsics.DisableAVX2);
#endif
}
}

2
tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs

@ -26,12 +26,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void RunSetCoeffsTest_Works() => RunSetCoeffsTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void RunSetCoeffsTest_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.AllowAll);
[Fact]
public void RunSetCoeffsTest_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.DisableHWIntrinsic);
#endif
}
}

8
tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs

@ -5,10 +5,8 @@ using System;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
#if SUPPORTS_RUNTIME_INTRINSICS
using SixLabors.ImageSharp.Tests.TestUtilities;
#endif
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Webp
{
@ -21,7 +19,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void CheckNonOpaque_WithNoneOpaquePixels_Works() => RunCheckNoneOpaqueWithNoneOpaquePixelsTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void CheckNonOpaque_WithOpaquePixels_WithHardwareIntrinsics_Works()
=> FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithOpaquePixelsTest, HwIntrinsics.AllowAll);
@ -45,7 +42,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void CheckNonOpaque_WithNoneOpaquePixels_WithoutAvx2_Works()
=> FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithNoneOpaquePixelsTest, HwIntrinsics.DisableAVX2);
#endif
private static void RunCheckNoneOpaqueWithNoneOpaquePixelsTest()
{
@ -200,7 +196,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
for (int length = 8; length < row.Length; length += 8)
{
// act
noneOpaque = WebpCommonUtils.CheckNonOpaque(row.Slice(0, length));
noneOpaque = WebpCommonUtils.CheckNonOpaque(row[..length]);
// assert
Assert.False(noneOpaque);

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

@ -44,14 +44,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
int expectedBitsPerPixel)
{
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
IImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
Assert.Equal(expectedWidth, imageInfo.Width);
Assert.Equal(expectedHeight, imageInfo.Height);
Assert.Equal(expectedBitsPerPixel, imageInfo.PixelType.BitsPerPixel);
}
using var stream = new MemoryStream(testFile.Bytes, false);
IImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
Assert.Equal(expectedWidth, imageInfo.Width);
Assert.Equal(expectedHeight, imageInfo.Height);
Assert.Equal(expectedBitsPerPixel, imageInfo.PixelType.BitsPerPixel);
}
[Theory]
@ -67,11 +65,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithoutFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -83,11 +79,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithSimpleFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -106,11 +100,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithComplexFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -121,11 +113,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_VerySmall<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -140,11 +130,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithPartitions<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -154,11 +142,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithSegmentation<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -171,11 +157,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithSharpnessLevel<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -196,11 +180,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithAlpha<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -208,11 +190,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithAlpha<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -221,11 +201,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithoutTransforms<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -240,11 +218,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -256,11 +232,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithColorIndexTransform<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -269,11 +243,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithPredictorTransform<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -282,11 +254,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithCrossColorTransform<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -306,11 +276,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithTwoTransforms<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -325,11 +293,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithThreeTransforms<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -337,18 +303,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void Decode_AnimatedLossless_VerifyAllFrames<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata();
WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
image.DebugSaveMultiFrame(provider);
image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact);
Assert.Equal(0, webpMetaData.AnimationLoopCount);
Assert.Equal(150U, frameMetaData.FrameDuration);
Assert.Equal(12, image.Frames.Count);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata();
WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
image.DebugSaveMultiFrame(provider);
image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact);
Assert.Equal(0, webpMetaData.AnimationLoopCount);
Assert.Equal(150U, frameMetaData.FrameDuration);
Assert.Equal(12, image.Frames.Count);
}
[Theory]
@ -356,18 +320,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void Decode_AnimatedLossy_VerifyAllFrames<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata();
WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
image.DebugSaveMultiFrame(provider);
image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Tolerant(0.04f));
Assert.Equal(0, webpMetaData.AnimationLoopCount);
Assert.Equal(150U, frameMetaData.FrameDuration);
Assert.Equal(12, image.Frames.Count);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata();
WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
image.DebugSaveMultiFrame(provider);
image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Tolerant(0.04f));
Assert.Equal(0, webpMetaData.AnimationLoopCount);
Assert.Equal(150U, frameMetaData.FrameDuration);
Assert.Equal(12, image.Frames.Count);
}
[Theory]
@ -375,10 +337,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void Decode_AnimatedLossless_WithFrameDecodingModeFirst_OnlyDecodesOneFrame<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(new WebpDecoder() { DecodingMode = FrameDecodingMode.First }))
{
Assert.Equal(1, image.Frames.Count);
}
using Image<TPixel> image = provider.GetImage(new WebpDecoder() { DecodingMode = FrameDecodingMode.First });
Assert.Equal(1, image.Frames.Count);
}
[Theory]
@ -389,10 +349,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
where TPixel : unmanaged, IPixel<TPixel>
{
// Just make sure no exception is thrown. The reference decoder fails to load the image.
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
}
// https://github.com/SixLabors/ImageSharp/issues/1594
@ -401,11 +359,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Issue1594<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<TPixel> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Theory]
@ -420,45 +376,36 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
}
});
#if SUPPORTS_RUNTIME_INTRINSICS
private static void RunDecodeLossyWithHorizontalFilter()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossyHorizontalFilterPath);
using (Image<Rgba32> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<Rgba32> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
private static void RunDecodeLossyWithVerticalFilter()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossyVerticalFilterPath);
using (Image<Rgba32> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<Rgba32> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
private static void RunDecodeLossyWithSimpleFilterTest()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossySimpleFilterPath);
using (Image<Rgba32> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<Rgba32> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
private static void RunDecodeLossyWithComplexFilterTest()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossyComplexFilterPath);
using (Image<Rgba32> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<Rgba32> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Fact]
@ -472,6 +419,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact]
public void DecodeLossyWithComplexFilterTest_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunDecodeLossyWithComplexFilterTest, HwIntrinsics.DisableHWIntrinsic);
#endif
}
}

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

@ -326,13 +326,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
image.VerifyEncoder(provider, "webp", string.Empty, encoder, ImageComparer.Tolerant(0.04f));
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void RunEncodeLossy_WithPeakImage_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunEncodeLossy_WithPeakImage, HwIntrinsics.AllowAll);
[Fact]
public void RunEncodeLossy_WithPeakImage_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunEncodeLossy_WithPeakImage, HwIntrinsics.DisableHWIntrinsic);
#endif
private static ImageComparer GetComparer(int quality)
{

19
tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs

@ -26,25 +26,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public static void RunUpSampleYuvToRgbTest()
{
var provider = TestImageProvider<Rgba32>.File(TestImageLossyFullPath);
using (Image<Rgba32> image = provider.GetImage(WebpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
using Image<Rgba32> image = provider.GetImage(WebpDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ReferenceDecoder);
}
[Fact]
public void UpSampleYuvToRgb_Works() => RunUpSampleYuvToRgbTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void UpSampleYuvToRgb_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.AllowAll);
[Fact]
public void UpSampleYuvToRgb_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.DisableSSE2);
#endif
[Theory]
[WithFile(TestImages.Webp.Yuv, PixelTypes.Rgba32)]
public void ConvertRgbToYuv_Works<TPixel>(TestImageProvider<TPixel> provider)
@ -157,8 +152,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
// assert
Assert.True(expectedY.AsSpan().SequenceEqual(y));
Assert.True(expectedU.AsSpan().SequenceEqual(u.Slice(0, expectedU.Length)));
Assert.True(expectedV.AsSpan().SequenceEqual(v.Slice(0, expectedV.Length)));
Assert.True(expectedU.AsSpan().SequenceEqual(u[..expectedU.Length]));
Assert.True(expectedV.AsSpan().SequenceEqual(v[..expectedV.Length]));
}
[Theory]
@ -263,8 +258,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
// assert
Assert.True(expectedY.AsSpan().SequenceEqual(y));
Assert.True(expectedU.AsSpan().SequenceEqual(u.Slice(0, expectedU.Length)));
Assert.True(expectedV.AsSpan().SequenceEqual(v.Slice(0, expectedV.Length)));
Assert.True(expectedU.AsSpan().SequenceEqual(u[..expectedU.Length]));
Assert.True(expectedV.AsSpan().SequenceEqual(v[..expectedV.Length]));
}
}
}

2
tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs

@ -89,7 +89,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Assert.Equal(mode, resizeOptions.Mode);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void HwIntrinsics_Resize()
{
@ -106,6 +105,5 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
RunTest,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableFMA);
}
#endif
}
}

17
tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs

@ -5,9 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86;
#endif
using Xunit;
using Xunit.Abstractions;
@ -55,7 +53,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
HwIntrinsics.AllowAll);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void CanLimitHwIntrinsicBaseFeatures()
{
@ -82,7 +79,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
AssertDisabled,
HwIntrinsics.DisableHWIntrinsic);
}
#endif
[Fact]
public void CanLimitHwIntrinsicFeaturesWithIntrinsicsParam()
@ -91,9 +87,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
{
Assert.NotNull(intrinsic);
switch ((HwIntrinsics)Enum.Parse(typeof(HwIntrinsics), intrinsic))
switch (Enum.Parse<HwIntrinsics>(intrinsic))
{
#if SUPPORTS_RUNTIME_INTRINSICS
case HwIntrinsics.DisableHWIntrinsic:
Assert.False(Sse.IsSupported);
Assert.False(Sse2.IsSupported);
@ -156,11 +151,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
case HwIntrinsics.DisableLZCNT:
Assert.False(Lzcnt.IsSupported);
break;
#endif
}
}
foreach (HwIntrinsics intrinsic in (HwIntrinsics[])Enum.GetValues(typeof(HwIntrinsics)))
foreach (HwIntrinsics intrinsic in Enum.GetValues<HwIntrinsics>())
{
FeatureTestRunner.RunWithHwIntrinsicsFeature(AssertHwIntrinsicsFeatureDisabled, intrinsic);
}
@ -173,10 +167,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
{
Assert.NotNull(serializable);
Assert.NotNull(FeatureTestRunner.DeserializeForXunit<FakeSerializable>(serializable));
#if SUPPORTS_RUNTIME_INTRINSICS
Assert.False(Sse.IsSupported);
#endif
}
FeatureTestRunner.RunWithHwIntrinsicsFeature(
@ -193,9 +184,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
Assert.NotNull(serializable);
Assert.NotNull(FeatureTestRunner.DeserializeForXunit<FakeSerializable>(serializable));
switch ((HwIntrinsics)Enum.Parse(typeof(HwIntrinsics), intrinsic))
switch (Enum.Parse<HwIntrinsics>(intrinsic))
{
#if SUPPORTS_RUNTIME_INTRINSICS
case HwIntrinsics.DisableHWIntrinsic:
Assert.False(Sse.IsSupported);
Assert.False(Sse2.IsSupported);
@ -258,7 +248,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
case HwIntrinsics.DisableLZCNT:
Assert.False(Lzcnt.IsSupported);
break;
#endif
}
}

Loading…
Cancel
Save