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> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\ImageSharp.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<!-- DynamicProxyGenAssembly2 is needed so Moq can use our internals --> <!-- DynamicProxyGenAssembly2 is needed so Moq can use our internals -->
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" Key="0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" /> <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.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.ColorSpaces.Companding 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 Length = Scale + 2; // 256kb @ 16bit precision.
private const int Scale = (1 << 16) - 1; 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++) for (int i = 0; i < result.Length; i++)
{ {
@ -49,10 +47,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
}, },
true); 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++) for (int i = 0; i < result.Length; i++)
{ {
@ -84,7 +82,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Expand(Span<Vector4> vectors) public static void Expand(Span<Vector4> vectors)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && vectors.Length >= 2) if (Avx2.IsSupported && vectors.Length >= 2)
{ {
CompandAvx2(vectors, ExpandTable); CompandAvx2(vectors, ExpandTable);
@ -96,7 +93,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
} }
} }
else else
#endif
{ {
CompandScalar(vectors, ExpandTable); CompandScalar(vectors, ExpandTable);
} }
@ -109,7 +105,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Compress(Span<Vector4> vectors) public static unsafe void Compress(Span<Vector4> vectors)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && vectors.Length >= 2) if (Avx2.IsSupported && vectors.Length >= 2)
{ {
CompandAvx2(vectors, CompressTable); CompandAvx2(vectors, CompressTable);
@ -121,7 +116,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
} }
} }
else else
#endif
{ {
CompandScalar(vectors, CompressTable); CompandScalar(vectors, CompressTable);
} }
@ -171,8 +165,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
public static float Compress(float channel) public static float Compress(float channel)
=> channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F; => channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F;
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe void CompandAvx2(Span<Vector4> vectors, float[] table) private static unsafe void CompandAvx2(Span<Vector4> vectors, float[] table)
{ {
@ -204,7 +196,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
} }
} }
} }
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe void CompandScalar(Span<Vector4> vectors, float[] table) 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); 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; public const MethodImplOptions AlwaysInline = MethodImplOptions.AggressiveInlining;
#if PROFILING #if PROFILING
public const MethodImplOptions HotPath = MethodImplOptions.NoInlining; public const MethodImplOptions HotPath = MethodImplOptions.NoInlining;
public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining; public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining;
#else #else
#if SUPPORTS_HOTPATH
public const MethodImplOptions HotPath = MethodImplOptions.AggressiveOptimization; public const MethodImplOptions HotPath = MethodImplOptions.AggressiveOptimization;
#else
public const MethodImplOptions HotPath = MethodImplOptions.AggressiveInlining;
#endif
public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining; public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining;
#endif #endif
public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining; public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining;

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

@ -5,10 +5,8 @@ using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
{ {
@ -18,20 +16,8 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
internal static class Numerics internal static class Numerics
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
public const int BlendAlphaControl = 0b_10_00_10_00; public const int BlendAlphaControl = 0b_10_00_10_00;
private const int ShuffleAlphaControl = 0b_11_11_11_11; 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> /// <summary>
/// Determine the Greatest CommonDivisor (GCD) of two numbers. /// Determine the Greatest CommonDivisor (GCD) of two numbers.
@ -129,13 +115,13 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Gaussian(float x, float sigma) 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 denominator = MathF.Sqrt(2 * MathF.PI) * sigma;
float exponentNumerator = -x * x; float exponentNumerator = -x * x;
float exponentDenominator = 2 * Pow2(sigma); float exponentDenominator = 2 * Pow2(sigma);
float left = Numerator / denominator; float left = numerator / denominator;
float right = MathF.Exp(exponentNumerator / exponentDenominator); float right = MathF.Exp(exponentNumerator / exponentDenominator);
return left * right; return left * right;
@ -300,7 +286,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clamp(Span<byte> span, byte min, byte max) 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) if (remainder.Length > 0)
{ {
@ -325,7 +311,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clamp(Span<uint> span, uint min, uint max) 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) if (remainder.Length > 0)
{ {
@ -350,7 +336,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clamp(Span<int> span, int min, int max) 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) if (remainder.Length > 0)
{ {
@ -375,7 +361,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clamp(Span<float> span, float min, float max) 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) if (remainder.Length > 0)
{ {
@ -400,7 +386,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clamp(Span<double> span, double min, double max) 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) if (remainder.Length > 0)
{ {
@ -427,7 +413,7 @@ namespace SixLabors.ImageSharp
if (adjustedCount > 0) if (adjustedCount > 0)
{ {
ClampImpl(span.Slice(0, adjustedCount), min, max); ClampImpl(span[..adjustedCount], min, max);
} }
return adjustedCount; return adjustedCount;
@ -512,7 +498,6 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Premultiply(Span<Vector4> vectors) public static void Premultiply(Span<Vector4> vectors)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && vectors.Length >= 2) if (Avx2.IsSupported && vectors.Length >= 2)
{ {
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float> // 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) if (Modulo2(vectors.Length) != 0)
{ {
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1. // 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 else
#endif
{ {
ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors); ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors);
ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, vectors.Length); ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, vectors.Length);
@ -555,7 +539,6 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnPremultiply(Span<Vector4> vectors) public static void UnPremultiply(Span<Vector4> vectors)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && vectors.Length >= 2) if (Avx2.IsSupported && vectors.Length >= 2)
{ {
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float> // 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) if (Modulo2(vectors.Length) != 0)
{ {
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1. // 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 else
#endif
{ {
ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors); ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors);
ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, vectors.Length); ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, vectors.Length);
@ -627,7 +609,6 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void CubeRootOnXYZ(Span<Vector4> vectors) public static unsafe void CubeRootOnXYZ(Span<Vector4> vectors)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse41.IsSupported) if (Sse41.IsSupported)
{ {
ref Vector128<float> vectors128Ref = ref Unsafe.As<Vector4, Vector128<float>>(ref MemoryMarshal.GetReference(vectors)); ref Vector128<float> vectors128Ref = ref Unsafe.As<Vector4, Vector128<float>>(ref MemoryMarshal.GetReference(vectors));
@ -678,7 +659,6 @@ namespace SixLabors.ImageSharp
} }
} }
else else
#endif
{ {
ref Vector4 vectorsRef = ref MemoryMarshal.GetReference(vectors); ref Vector4 vectorsRef = ref MemoryMarshal.GetReference(vectors);
ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsRef, vectors.Length); ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsRef, vectors.Length);
@ -727,8 +707,6 @@ namespace SixLabors.ImageSharp
} }
} }
#if SUPPORTS_RUNTIME_INTRINSICS
/// <summary> /// <summary>
/// Performs a linear interpolation between two values based on the given weighting. /// Performs a linear interpolation between two values based on the given weighting.
/// </summary> /// </summary>
@ -752,7 +730,6 @@ namespace SixLabors.ImageSharp
return Avx.Add(Avx.Multiply(diff, amount), value1); return Avx.Add(Avx.Multiply(diff, amount), value1);
} }
} }
#endif
/// <summary> /// <summary>
/// Performs a linear interpolation between two values based on the given weighting. /// 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) public static float Lerp(float value1, float value2, float amount)
=> ((value2 - value1) * amount) + value1; => ((value2 - value1) * amount) + value1;
#if SUPPORTS_RUNTIME_INTRINSICS
/// <summary> /// <summary>
/// Accumulates 8-bit integers into <paramref name="accumulator"/> by /// Accumulates 8-bit integers into <paramref name="accumulator"/> by
/// widening them to 32-bit integers and performing four additions. /// 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 // Vector128<int>.ToScalar() isn't optimized pre-net5.0 https://github.com/dotnet/runtime/pull/37882
return Sse2.ConvertToInt32(vsum); return Sse2.ConvertToInt32(vsum);
} }
#endif
/// <summary> /// <summary>
/// Calculates floored log of the specified value, base 2. /// Calculates floored log of the specified value, base 2.
/// Note that by convention, input value 0 returns 0 since Log(0) is undefined. /// Note that by convention, input value 0 returns 0 since Log(0) is undefined.
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
public static int Log2(uint value) [MethodImpl(MethodImplOptions.AggressiveInlining)]
{ public static int Log2(uint value) => BitOperations.Log2(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
/// <summary> /// <summary>
/// Fast division with ceiling for <see cref="uint"/> numbers. /// 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> /// <param name="offset">The number of bits to rotate with.</param>
/// <returns>The rotated value.</returns> /// <returns>The rotated value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint RotateLeft(uint value, int offset) public static uint RotateLeft(uint value, int offset) => BitOperations.RotateLeft(value, 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
}
#if !SUPPORTS_BITOPERATIONS
/// <summary> /// <summary>
/// Rotates the specified value right by the specified number of bits. /// Rotates the specified value right by the specified number of bits.
/// </summary> /// </summary>
@ -966,9 +868,7 @@ namespace SixLabors.ImageSharp
/// <param name="offset">The number of bits to rotate with.</param> /// <param name="offset">The number of bits to rotate with.</param>
/// <returns>The rotated value.</returns> /// <returns>The rotated value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint RotateRightSoftwareFallback(uint value, int offset) public static uint RotateRight(uint value, int offset) => BitOperations.RotateRight(value, offset);
=> (value >> offset) | (value << (32 - offset));
#endif
/// <summary> /// <summary>
/// Tells whether input value is outside of the given range. /// 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> /// </summary>
public static class ExtendedIntrinsics public static class ExtendedIntrinsics
{ {
public static bool IsAvailable { get; } = public static bool IsAvailable { get; } = Vector.IsHardwareAccelerated;
#if SUPPORTS_EXTENDED_INTRINSICS
Vector.IsHardwareAccelerated;
#else
false;
#endif
/// <summary> /// <summary>
/// Widen and convert a vector of <see cref="short"/> values into 2 vectors of <see cref="float"/>-s. /// 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) if (adjustedCount > 0)
{ {
ByteToNormalizedFloat(source.Slice(0, adjustedCount), dest.Slice(0, adjustedCount)); ByteToNormalizedFloat(source[..adjustedCount], dest[..adjustedCount]);
source = source.Slice(adjustedCount); source = source[adjustedCount..];
dest = dest.Slice(adjustedCount); dest = dest[adjustedCount..];
} }
} }
@ -89,12 +84,10 @@ namespace SixLabors.ImageSharp
if (adjustedCount > 0) if (adjustedCount > 0)
{ {
NormalizedFloatToByteSaturate( NormalizedFloatToByteSaturate(source[..adjustedCount], dest[..adjustedCount]);
source.Slice(0, adjustedCount),
dest.Slice(0, adjustedCount));
source = source.Slice(adjustedCount); source = source[adjustedCount..];
dest = dest.Slice(adjustedCount); dest = dest[adjustedCount..];
} }
} }
@ -160,12 +153,10 @@ namespace SixLabors.ImageSharp
Vector<uint> w2 = ConvertToUInt32(f2); Vector<uint> w2 = ConvertToUInt32(f2);
Vector<uint> w3 = ConvertToUInt32(f3); Vector<uint> w3 = ConvertToUInt32(f3);
Vector<ushort> u0 = Vector.Narrow(w0, w1); var u0 = Vector.Narrow(w0, w1);
Vector<ushort> u1 = Vector.Narrow(w2, w3); var u1 = Vector.Narrow(w2, w3);
Vector<byte> b = Vector.Narrow(u0, u1); Unsafe.Add(ref destBase, i) = Vector.Narrow(u0, u1);
Unsafe.Add(ref destBase, i) = b;
} }
} }
@ -176,15 +167,15 @@ namespace SixLabors.ImageSharp
vf *= maxBytes; vf *= maxBytes;
vf += new Vector<float>(0.5f); vf += new Vector<float>(0.5f);
vf = Vector.Min(Vector.Max(vf, Vector<float>.Zero), maxBytes); 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); return Vector.AsVectorUInt32(vi);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector<float> ConvertToSingle(Vector<uint> u) private static Vector<float> ConvertToSingle(Vector<uint> u)
{ {
Vector<int> vi = Vector.AsVectorInt32(u); var vi = Vector.AsVectorInt32(u);
Vector<float> v = Vector.ConvertToSingle(vi); var v = Vector.ConvertToSingle(vi);
v *= new Vector<float>(1f / 255f); v *= new Vector<float>(1f / 255f);
return v; return v;
} }

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

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

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

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

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

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

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

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

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

@ -6,11 +6,8 @@ using System.Diagnostics;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
{ {
@ -56,8 +53,6 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector<float> FastRound(this Vector<float> v) internal static Vector<float> FastRound(this Vector<float> v)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
ref Vector256<float> v256 = ref Unsafe.As<Vector<float>, Vector256<float>>(ref v); 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); return Unsafe.As<Vector256<float>, Vector<float>>(ref vRound);
} }
else else
#endif
{ {
var magic0 = new Vector<int>(int.MinValue); // 0x80000000 var magic0 = new Vector<int>(int.MinValue); // 0x80000000
var sgn0 = Vector.AsVectorSingle(magic0); var sgn0 = Vector.AsVectorSingle(magic0);
@ -87,13 +81,8 @@ namespace SixLabors.ImageSharp
internal static void ByteToNormalizedFloat(ReadOnlySpan<byte> source, Span<float> dest) 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!"); 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); 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: // Also deals with the remainder from previous conversions:
FallbackIntrinsics128.ByteToNormalizedFloatReduce(ref source, ref dest); 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!"); 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); 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: // Also deals with the remainder from previous conversions:
FallbackIntrinsics128.NormalizedFloatToByteSaturateReduce(ref source, ref dest); FallbackIntrinsics128.NormalizedFloatToByteSaturateReduce(ref source, ref dest);

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

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

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

@ -4,10 +4,8 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Compression.Zlib namespace SixLabors.ImageSharp.Compression.Zlib
{ {
@ -22,7 +20,6 @@ namespace SixLabors.ImageSharp.Compression.Zlib
/// </summary> /// </summary>
public const uint SeedValue = 0U; public const uint SeedValue = 0U;
#if SUPPORTS_RUNTIME_INTRINSICS
private const int MinBufferSize = 64; private const int MinBufferSize = 64;
private const int ChunksizeMask = 15; private const int ChunksizeMask = 15;
@ -35,7 +32,6 @@ namespace SixLabors.ImageSharp.Compression.Zlib
0x0163cd6124, 0x0000000000, // k5, k0 0x0163cd6124, 0x0000000000, // k5, k0
0x01db710641, 0x01f7011641 // polynomial 0x01db710641, 0x01f7011641 // polynomial
}; };
#endif
/// <summary> /// <summary>
/// Calculates the CRC checksum with the bytes taken from the span. /// Calculates the CRC checksum with the bytes taken from the span.
@ -60,21 +56,14 @@ namespace SixLabors.ImageSharp.Compression.Zlib
return crc; return crc;
} }
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse41.IsSupported && Pclmulqdq.IsSupported && buffer.Length >= MinBufferSize) if (Sse41.IsSupported && Pclmulqdq.IsSupported && buffer.Length >= MinBufferSize)
{ {
return ~CalculateSse(~crc, buffer); return ~CalculateSse(~crc, buffer);
} }
else
{
return ~CalculateScalar(~crc, buffer);
}
#else
return ~CalculateScalar(~crc, buffer); return ~CalculateScalar(~crc, buffer);
#endif
} }
#if SUPPORTS_RUNTIME_INTRINSICS
// Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/crc32_simd.c // Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/crc32_simd.c
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static unsafe uint CalculateSse(uint crc, ReadOnlySpan<byte> buffer) private static unsafe uint CalculateSse(uint crc, ReadOnlySpan<byte> buffer)
@ -194,11 +183,10 @@ namespace SixLabors.ImageSharp.Compression.Zlib
x1 = Sse2.Xor(x1, x2); x1 = Sse2.Xor(x1, x2);
crc = (uint)Sse41.Extract(x1.AsInt32(), 1); 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)] [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static uint CalculateScalar(uint crc, ReadOnlySpan<byte> buffer) 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.lookahead = 0;
this.prevAvailable = false; this.prevAvailable = false;
this.matchLen = DeflaterConstants.MIN_MATCH - 1; this.matchLen = DeflaterConstants.MIN_MATCH - 1;
this.head.Span.Slice(0, DeflaterConstants.HASH_SIZE).Clear(); this.head.Span[..DeflaterConstants.HASH_SIZE].Clear();
this.prev.Span.Slice(0, DeflaterConstants.WSIZE).Clear(); this.prev.Span[..DeflaterConstants.WSIZE].Clear();
} }
/// <summary> /// <summary>
@ -286,7 +286,7 @@ namespace SixLabors.ImageSharp.Compression.Zlib
/// <param name="level">The value to set the level to.</param> /// <param name="level">The value to set the level to.</param>
public void SetLevel(int level) public void SetLevel(int level)
{ {
if ((level < 0) || (level > 9)) if (level is < 0 or > 9)
{ {
DeflateThrowHelper.ThrowOutOfRange(nameof(level)); DeflateThrowHelper.ThrowOutOfRange(nameof(level));
} }

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

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

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

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

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -43,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
ref Vector256<float> bBase = ref b.V0; ref Vector256<float> bBase = ref b.V0;
ref Vector256<short> destRef = ref dest.V01; 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) 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.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
using System.Text; using System.Text;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
@ -160,7 +158,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void MultiplyInPlace(float value) public void MultiplyInPlace(float value)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported) if (Avx.IsSupported)
{ {
var valueVec = Vector256.Create(value); var valueVec = Vector256.Create(value);
@ -174,7 +171,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
this.V7 = Avx.Multiply(this.V7, valueVec); this.V7 = Avx.Multiply(this.V7, valueVec);
} }
else else
#endif
{ {
var valueVec = new Vector4(value); var valueVec = new Vector4(value);
this.V0L *= valueVec; this.V0L *= valueVec;
@ -202,7 +198,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public unsafe void MultiplyInPlace(ref Block8x8F other) public unsafe void MultiplyInPlace(ref Block8x8F other)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported) if (Avx.IsSupported)
{ {
this.V0 = Avx.Multiply(this.V0, other.V0); 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); this.V7 = Avx.Multiply(this.V7, other.V7);
} }
else else
#endif
{ {
this.V0L *= other.V0L; this.V0L *= other.V0L;
this.V0R *= other.V0R; this.V0R *= other.V0R;
@ -243,7 +237,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void AddInPlace(float value) public void AddInPlace(float value)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported) if (Avx.IsSupported)
{ {
var valueVec = Vector256.Create(value); var valueVec = Vector256.Create(value);
@ -257,7 +250,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
this.V7 = Avx.Add(this.V7, valueVec); this.V7 = Avx.Add(this.V7, valueVec);
} }
else else
#endif
{ {
var valueVec = new Vector4(value); var valueVec = new Vector4(value);
this.V0L += valueVec; this.V0L += valueVec;
@ -287,7 +279,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <param name="qt">The quantization table.</param> /// <param name="qt">The quantization table.</param>
public static void Quantize(ref Block8x8F block, ref Block8x8 dest, ref Block8x8F qt) public static void Quantize(ref Block8x8F block, ref Block8x8 dest, ref Block8x8F qt)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
MultiplyIntoInt16_Avx2(ref block, ref qt, ref dest); MultiplyIntoInt16_Avx2(ref block, ref qt, ref dest);
@ -299,7 +290,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
ZigZag.ApplyTransposingZigZagOrderingSsse3(ref dest); ZigZag.ApplyTransposingZigZagOrderingSsse3(ref dest);
} }
else else
#endif
{ {
for (int i = 0; i < Size; i++) for (int i = 0; i < Size; i++)
{ {
@ -366,13 +356,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void LoadFrom(ref Block8x8 source) public void LoadFrom(ref Block8x8 source)
{ {
#if SUPPORTS_EXTENDED_INTRINSICS
if (SimdUtils.HasVector8) if (SimdUtils.HasVector8)
{ {
this.LoadFromInt16ExtendedAvx2(ref source); this.LoadFromInt16ExtendedAvx2(ref source);
return; return;
} }
#endif
this.LoadFromInt16Scalar(ref source); this.LoadFromInt16Scalar(ref source);
} }
@ -414,7 +403,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <param name="value">Value to compare to.</param> /// <param name="value">Value to compare to.</param>
public bool EqualsToScalar(int value) public bool EqualsToScalar(int value)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
const int equalityMask = unchecked((int)0b1111_1111_1111_1111_1111_1111_1111_1111); 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; 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 /> /// <inheritdoc />
@ -491,13 +477,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void TransposeInplace() public void TransposeInplace()
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported) if (Avx.IsSupported)
{ {
this.TransposeInplace_Avx(); this.TransposeInplace_Avx();
} }
else else
#endif
{ {
this.TransposeInplace_Scalar(); this.TransposeInplace_Scalar();
} }

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; 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> /// </summary>
private static IEnumerable<JpegColorConverterBase> GetYCbCrConverters(int precision) private static IEnumerable<JpegColorConverterBase> GetYCbCrConverters(int precision)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromYCbCrAvx(precision); yield return new FromYCbCrAvx(precision);
#endif
yield return new FromYCbCrVector(precision); yield return new FromYCbCrVector(precision);
yield return new FromYCbCrScalar(precision); yield return new FromYCbCrScalar(precision);
} }
@ -129,9 +127,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary> /// </summary>
private static IEnumerable<JpegColorConverterBase> GetYccKConverters(int precision) private static IEnumerable<JpegColorConverterBase> GetYccKConverters(int precision)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromYccKAvx(precision); yield return new FromYccKAvx(precision);
#endif
yield return new FromYccKVector(precision); yield return new FromYccKVector(precision);
yield return new FromYccKScalar(precision); yield return new FromYccKScalar(precision);
} }
@ -141,9 +137,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary> /// </summary>
private static IEnumerable<JpegColorConverterBase> GetCmykConverters(int precision) private static IEnumerable<JpegColorConverterBase> GetCmykConverters(int precision)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromCmykAvx(precision); yield return new FromCmykAvx(precision);
#endif
yield return new FromCmykVector(precision); yield return new FromCmykVector(precision);
yield return new FromCmykScalar(precision); yield return new FromCmykScalar(precision);
} }
@ -153,9 +147,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary> /// </summary>
private static IEnumerable<JpegColorConverterBase> GetGrayScaleConverters(int precision) private static IEnumerable<JpegColorConverterBase> GetGrayScaleConverters(int precision)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromGrayscaleAvx(precision); yield return new FromGrayscaleAvx(precision);
#endif
yield return new FromGrayScaleVector(precision); yield return new FromGrayScaleVector(precision);
yield return new FromGrayscaleScalar(precision); yield return new FromGrayscaleScalar(precision);
} }
@ -165,9 +157,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary> /// </summary>
private static IEnumerable<JpegColorConverterBase> GetRgbConverters(int precision) private static IEnumerable<JpegColorConverterBase> GetRgbConverters(int precision)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
yield return new FromRgbAvx(precision); yield return new FromRgbAvx(precision);
#endif
yield return new FromRgbVector(precision); yield return new FromRgbVector(precision);
yield return new FromRgbScalar(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) internal static int GetHuffmanEncodingLength(uint value)
{ {
DebugGuard.IsTrue(value <= (1 << 16), "Huffman encoder is supposed to encode a value of 16bit size max"); 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 // 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)) // 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 // 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 // But most modern CPUs support this instruction so this should not be a problem
return 32 - BitOperations.LeadingZeroCount(value); 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> /// <summary>

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

@ -5,10 +5,8 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats; 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"); 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 Vector128<byte> l8ByteSpan = ref Unsafe.As<L8, Vector128<byte>>(ref l8Start);
ref Vector256<float> destRef = ref yBlock.V0; ref Vector256<float> destRef = ref yBlock.V0;
const int bytesPerL8Stride = 8; const int bytesPerL8Stride = 8;
for (nint i = 0; i < 8; i++) 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> /// <summary>

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

@ -5,10 +5,8 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats; 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"); 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<byte> rgbByteSpan = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(rgbSpan));
ref Vector256<float> redRef = ref rBlock.V0; ref Vector256<float> redRef = ref rBlock.V0;
ref Vector256<float> greenRef = ref gBlock.V0; ref Vector256<float> greenRef = ref gBlock.V0;
ref Vector256<float> blueRef = ref bBlock.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)); Vector256<uint> extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.MoveFirst24BytesToSeparateLanes));
var extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.ExtractRgb)); Vector256<byte> extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.ExtractRgb));
Vector256<byte> rgb, rg, bx; Vector256<byte> rgb, rg, bx;
const int bytesPerRgbStride = 24; 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 greenRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32());
Unsafe.Add(ref blueRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, 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) 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;
using System.Diagnostics; using System.Diagnostics;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{ {
internal static class RgbToYCbCrConverterVectorized internal static class RgbToYCbCrConverterVectorized
{ {
public static bool IsSupported public static bool IsSupported => Avx2.IsSupported;
{
get
{
#if SUPPORTS_RUNTIME_INTRINSICS
return Avx2.IsSupported;
#else
return false;
#endif
}
}
public static int AvxCompatibilityPadding 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 // 8 byte padding to rgb byte span will solve this problem without extra code in converters
get get
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (IsSupported) if (IsSupported)
{ {
return 8; return 8;
} }
#endif
return 0; return 0;
} }
} }
#if SUPPORTS_RUNTIME_INTRINSICS
internal static ReadOnlySpan<byte> MoveFirst24BytesToSeparateLanes => new byte[] internal static ReadOnlySpan<byte> MoveFirst24BytesToSeparateLanes => new byte[]
{ {
0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 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,
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> /// <summary>
/// Converts 8x8 Rgb24 pixel matrix to YCbCr pixel matrices with 4:4:4 subsampling /// 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"); Debug.Assert(IsSupported, "AVX2 is required to run this converter");
#if SUPPORTS_RUNTIME_INTRINSICS
var f0299 = Vector256.Create(0.299f); var f0299 = Vector256.Create(0.299f);
var f0587 = Vector256.Create(0.587f); var f0587 = Vector256.Create(0.587f);
var f0114 = Vector256.Create(0.114f); var f0114 = Vector256.Create(0.114f);
@ -95,15 +78,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
var fn0418688 = Vector256.Create(-0.418688f); var fn0418688 = Vector256.Create(-0.418688f);
var fn0081312F = Vector256.Create(-0.081312F); var fn0081312F = Vector256.Create(-0.081312F);
var f05 = Vector256.Create(0.5f); 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<byte> rgbByteSpan = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(rgbSpan));
ref Vector256<float> destYRef = ref yBlock.V0; ref Vector256<float> destYRef = ref yBlock.V0;
ref Vector256<float> destCbRef = ref cbBlock.V0; ref Vector256<float> destCbRef = ref cbBlock.V0;
ref Vector256<float> destCrRef = ref crBlock.V0; ref Vector256<float> destCrRef = ref crBlock.V0;
var extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes)); Vector256<uint> extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes));
var extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb)); Vector256<byte> extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb));
Vector256<byte> rgb, rg, bx; Vector256<byte> rgb, rg, bx;
Vector256<float> r, g, b; 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)) // 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)); Unsafe.Add(ref destCrRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r));
} }
#endif
} }
/// <summary> /// <summary>
@ -140,7 +122,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
{ {
Debug.Assert(IsSupported, "AVX2 is required to run this converter"); Debug.Assert(IsSupported, "AVX2 is required to run this converter");
#if SUPPORTS_RUNTIME_INTRINSICS
var f0299 = Vector256.Create(0.299f); var f0299 = Vector256.Create(0.299f);
var f0587 = Vector256.Create(0.587f); var f0587 = Vector256.Create(0.587f);
var f0114 = Vector256.Create(0.114f); var f0114 = Vector256.Create(0.114f);
@ -150,7 +131,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
var fn0418688 = Vector256.Create(-0.418688f); var fn0418688 = Vector256.Create(-0.418688f);
var fn0081312F = Vector256.Create(-0.081312F); var fn0081312F = Vector256.Create(-0.081312F);
var f05 = Vector256.Create(0.5f); 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<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> 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); 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)); Vector256<uint> extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes));
var extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb)); Vector256<byte> extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb));
Vector256<byte> rgb, rg, bx; Vector256<byte> rgb, rg, bx;
Vector256<float> r, g, b; 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)) // 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)); 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> /// <summary>
/// Scales 16x2 matrix to 8x1 using 2x2 average /// Scales 16x2 matrix to 8x1 using 2x2 average
/// </summary> /// </summary>
@ -254,6 +233,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
return Avx2.Permute4x64(avg2x2.AsDouble(), 0b11_01_10_00).AsSingle(); 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. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
@ -45,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
block.V0 = Avx.Add(tmp10, tmp11); block.V0 = Avx.Add(tmp10, tmp11);
block.V4 = Avx.Subtract(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); Vector256<float> z1 = Avx.Multiply(Avx.Add(tmp12, tmp13), mm256_F_0_7071);
block.V2 = Avx.Add(tmp13, z1); block.V2 = Avx.Add(tmp13, z1);
block.V6 = Avx.Subtract(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> tmp10 = Avx.Add(z5, tmp2);
Vector256<float> tmp11 = Avx.Subtract(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> tmp13 = Avx.Add(tmp1, tmp3);
Vector256<float> tmp12 = SimdUtils.HwIntrinsics.MultiplySubstract(tmp13, Avx.Subtract(tmp1, tmp3), mm256_F_1_4142); 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.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components namespace SixLabors.ImageSharp.Formats.Jpeg.Components
@ -112,13 +110,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <param name="block">Input block.</param> /// <param name="block">Input block.</param>
public static void TransformIDCT(ref Block8x8F block) public static void TransformIDCT(ref Block8x8F block)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported) if (Avx.IsSupported)
{ {
IDCT8x8_Avx(ref block); IDCT8x8_Avx(ref block);
} }
else else
#endif
{ {
IDCT_Vector4(ref block); IDCT_Vector4(ref block);
} }
@ -134,13 +130,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <param name="block">Input block.</param> /// <param name="block">Input block.</param>
public static void TransformFDCT(ref Block8x8F block) public static void TransformFDCT(ref Block8x8F block)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported) if (Avx.IsSupported)
{ {
FDCT8x8_Avx(ref block); FDCT8x8_Avx(ref block);
} }
else else
#endif
{ {
FDCT_Vector4(ref block); FDCT_Vector4(ref block);
} }

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

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; 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;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Formats.Png.Filters namespace SixLabors.ImageSharp.Formats.Png.Filters
{ {
@ -35,19 +32,16 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// With pixels positioned like this: // With pixels positioned like this:
// prev: c b // prev: c b
// row: a d // row: a d
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported && bytesPerPixel is 4) if (Sse2.IsSupported && bytesPerPixel is 4)
{ {
DecodeSse2(scanline, previousScanline); DecodeSse2(scanline, previousScanline);
} }
else else
#endif
{ {
DecodeScalar(scanline, previousScanline, bytesPerPixel); DecodeScalar(scanline, previousScanline, bytesPerPixel);
} }
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeSse2(Span<byte> scanline, Span<byte> previousScanline) private static void DecodeSse2(Span<byte> scanline, Span<byte> previousScanline)
{ {
@ -81,7 +75,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
offset += 4; offset += 4;
} }
} }
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline, int bytesPerPixel) 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)); sum += Numerics.Abs(unchecked((sbyte)res));
} }
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
Vector256<byte> zero = Vector256<byte>.Zero; Vector256<byte> zero = Vector256<byte>.Zero;
@ -210,7 +202,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += Numerics.ReduceSum(sumAccumulator); 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 */) 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.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Formats.Png.Filters namespace SixLabors.ImageSharp.Formats.Png.Filters
{ {
@ -38,20 +35,16 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// row: a d // row: a d
// The Paeth function predicts d to be whichever of a, b, or c is nearest to // The Paeth function predicts d to be whichever of a, b, or c is nearest to
// p = a + b - c. // p = a + b - c.
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse41.IsSupported && bytesPerPixel is 4) if (Sse41.IsSupported && bytesPerPixel is 4)
{ {
DecodeSse41(scanline, previousScanline); DecodeSse41(scanline, previousScanline);
} }
else else
#endif
{ {
DecodeScalar(scanline, previousScanline, bytesPerPixel); DecodeScalar(scanline, previousScanline, bytesPerPixel);
} }
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeSse41(Span<byte> scanline, Span<byte> previousScanline) private static void DecodeSse41(Span<byte> scanline, Span<byte> previousScanline)
{ {
@ -107,8 +100,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
} }
} }
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline, int bytesPerPixel) 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)); sum += Numerics.Abs(unchecked((sbyte)res));
} }
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
Vector256<byte> zero = Vector256<byte>.Zero; Vector256<byte> zero = Vector256<byte>.Zero;
@ -213,7 +203,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += (int)sumAccumulator[i]; 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 */) 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; return upperLeft;
} }
#if SUPPORTS_RUNTIME_INTRINSICS
private static Vector256<byte> PaethPredictor(Vector256<byte> left, Vector256<byte> above, Vector256<byte> upleft) private static Vector256<byte> PaethPredictor(Vector256<byte> left, Vector256<byte> above, Vector256<byte> upleft)
{ {
Vector256<byte> zero = Vector256<byte>.Zero; Vector256<byte> zero = Vector256<byte>.Zero;
@ -324,6 +312,5 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
left: above, left: above,
right: upperLeft)); right: upperLeft));
} }
#endif
} }
} }

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

@ -5,11 +5,8 @@ using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Formats.Png.Filters 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) public static void Decode(Span<byte> scanline, int bytesPerPixel)
{ {
// The Sub filter predicts each pixel as the previous pixel. // The Sub filter predicts each pixel as the previous pixel.
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported && bytesPerPixel is 4) if (Sse2.IsSupported && bytesPerPixel is 4)
{ {
DecodeSse2(scanline); DecodeSse2(scanline);
} }
else else
#endif
{ {
DecodeScalar(scanline, bytesPerPixel); DecodeScalar(scanline, bytesPerPixel);
} }
} }
#if SUPPORTS_RUNTIME_INTRINSICS
private static void DecodeSse2(Span<byte> scanline) private static void DecodeSse2(Span<byte> scanline)
{ {
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
@ -64,7 +58,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
offset += 4; offset += 4;
} }
} }
#endif
private static void DecodeScalar(Span<byte> scanline, int bytesPerPixel) 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)); sum += Numerics.Abs(unchecked((sbyte)res));
} }
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
Vector256<byte> zero = Vector256<byte>.Zero; Vector256<byte> zero = Vector256<byte>.Zero;
@ -151,7 +143,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += (int)sumAccumulator[i]; 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 */) 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 += 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.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Formats.Png.Filters namespace SixLabors.ImageSharp.Formats.Png.Filters
{ {
@ -30,7 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
{ {
DebugGuard.MustBeSameSized<byte>(scanline, previousScanline, nameof(scanline)); DebugGuard.MustBeSameSized<byte>(scanline, previousScanline, nameof(scanline));
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
DecodeAvx2(scanline, previousScanline); DecodeAvx2(scanline, previousScanline);
@ -40,13 +36,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
DecodeSse2(scanline, previousScanline); DecodeSse2(scanline, previousScanline);
} }
else else
#endif
{ {
DecodeScalar(scanline, previousScanline); DecodeScalar(scanline, previousScanline);
} }
} }
#if SUPPORTS_RUNTIME_INTRINSICS
private static void DecodeAvx2(Span<byte> scanline, Span<byte> previousScanline) private static void DecodeAvx2(Span<byte> scanline, Span<byte> previousScanline)
{ {
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
@ -110,7 +104,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
offset++; offset++;
} }
} }
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline) private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline)
@ -150,7 +143,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
int x = 0; int x = 0;
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
Vector256<byte> zero = Vector256<byte>.Zero; Vector256<byte> zero = Vector256<byte>.Zero;
@ -191,7 +183,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
sum += (int)sumAccumulator[i]; sum += (int)sumAccumulator[i];
} }
} }
#endif
for (; x < scanline.Length; /* Note: ++x happens in the body to avoid one add operation */) 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.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using SixLabors.ImageSharp.Formats.Webp.BitReader; using SixLabors.ImageSharp.Formats.Webp.BitReader;
using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Formats.Webp.Lossless;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Formats.Webp namespace SixLabors.ImageSharp.Formats.Webp
{ {
@ -143,7 +141,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
Span<byte> alphaSpan = this.Alpha.Memory.Span; Span<byte> alphaSpan = this.Alpha.Memory.Span;
if (this.AlphaFilterType == WebpAlphaFilterType.None) if (this.AlphaFilterType == WebpAlphaFilterType.None)
{ {
dataSpan.Slice(0, pixelCount).CopyTo(alphaSpan); dataSpan[..pixelCount].CopyTo(alphaSpan);
return; return;
} }
@ -166,8 +164,8 @@ namespace SixLabors.ImageSharp.Formats.Webp
} }
prev = dst; prev = dst;
deltas = deltas.Slice(this.Width); deltas = deltas[this.Width..];
dst = dst.Slice(this.Width); dst = dst[this.Width..];
} }
} }
else else
@ -199,7 +197,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
} }
Span<byte> alphaSpan = this.Alpha.Memory.Span; 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++) for (int y = firstRow; y < lastRow; y++)
{ {
switch (this.AlphaFilterType) switch (this.AlphaFilterType)
@ -216,7 +214,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
} }
prev = dst; prev = dst;
dst = dst.Slice(stride); dst = dst[stride..];
} }
this.PrevRow = lastRow - 1; this.PrevRow = lastRow - 1;
@ -234,8 +232,8 @@ namespace SixLabors.ImageSharp.Formats.Webp
Span<byte> output = this.Alpha.Memory.Span; Span<byte> output = this.Alpha.Memory.Span;
Span<uint> pixelData = this.Vp8LDec.Pixels.Memory.Span; Span<uint> pixelData = this.Vp8LDec.Pixels.Memory.Span;
Span<byte> pixelDataAsBytes = MemoryMarshal.Cast<uint, byte>(pixelData); Span<byte> pixelDataAsBytes = MemoryMarshal.Cast<uint, byte>(pixelData);
Span<byte> dst = output.Slice(this.Width * firstRow); Span<byte> dst = output[(this.Width * firstRow)..];
Span<byte> input = pixelDataAsBytes.Slice(this.Vp8LDec.Width * firstRow); Span<byte> input = pixelDataAsBytes[(this.Vp8LDec.Width * firstRow)..];
if (this.Vp8LDec.Transforms.Count == 0 || this.Vp8LDec.Transforms[0].TransformType != Vp8LTransformType.ColorIndexingTransform) 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) private static void HorizontalUnfilter(Span<byte> prev, Span<byte> input, Span<byte> dst, int width)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
dst[0] = (byte)(input[0] + (prev.IsEmpty ? 0 : prev[0])); dst[0] = (byte)(input[0] + (prev.IsEmpty ? 0 : prev[0]));
@ -345,7 +342,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
} }
} }
else else
#endif
{ {
byte pred = (byte)(prev.IsEmpty ? 0 : prev[0]); byte pred = (byte)(prev.IsEmpty ? 0 : prev[0]);
@ -366,7 +362,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
} }
else else
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
nint i; nint i;
@ -386,7 +381,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
} }
} }
else else
#endif
{ {
for (int i = 0; i < width; i++) for (int i = 0; i < width; i++)
{ {

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

@ -4,10 +4,8 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Formats.Webp.Lossless 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) 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) if (Avx2.IsSupported && tileWidth >= 16)
{ {
const int span = 16; const int span = 16;
Span<ushort> values = stackalloc ushort[span]; 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); 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);
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); 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);
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); 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);
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); 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);
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); 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);
Vector256<short> multsr = Vector256.Create(LosslessUtils.Cst5b(redToBlue)); var multsr = Vector256.Create(LosslessUtils.Cst5b(redToBlue));
Vector256<short> multsg = Vector256.Create(LosslessUtils.Cst5b(greenToBlue)); var multsg = Vector256.Create(LosslessUtils.Cst5b(greenToBlue));
for (int y = 0; y < tileHeight; y++) 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); ref uint inputRef = ref MemoryMarshal.GetReference(srcSpan);
for (nint x = 0; x <= tileWidth - span; x += span) for (nint x = 0; x <= tileWidth - span; x += span)
{ {
@ -63,23 +60,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int leftOver = tileWidth & (span - 1); int leftOver = tileWidth & (span - 1);
if (leftOver > 0) 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) else if (Sse41.IsSupported)
{ {
const int span = 8; const int span = 8;
Span<ushort> values = stackalloc ushort[span]; 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); var 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); var 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); var 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); var 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); var 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)); var multsr = Vector128.Create(LosslessUtils.Cst5b(redToBlue));
Vector128<short> multsg = Vector128.Create(LosslessUtils.Cst5b(greenToBlue)); var multsg = Vector128.Create(LosslessUtils.Cst5b(greenToBlue));
for (int y = 0; y < tileHeight; y++) 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); ref uint inputRef = ref MemoryMarshal.GetReference(srcSpan);
for (nint x = 0; x <= tileWidth - span; x += span) for (nint x = 0; x <= tileWidth - span; x += span)
{ {
@ -113,11 +110,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int leftOver = tileWidth & (span - 1); int leftOver = tileWidth & (span - 1);
if (leftOver > 0) 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 else
#endif
{ {
CollectColorBlueTransformsNoneVectorized(bgra, stride, tileWidth, tileHeight, greenToBlue, redToBlue, histo); 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) 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) if (Avx2.IsSupported && tileWidth >= 16)
{ {
Vector256<byte> collectColorRedTransformsGreenMask256 = Vector256.Create(0x00ff00).AsByte(); Vector256<byte> collectColorRedTransformsGreenMask256 = Vector256.Create(0x00ff00).AsByte();
Vector256<byte> collectColorRedTransformsAndMask256 = Vector256.Create((short)0xff).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; const int span = 16;
Span<ushort> values = stackalloc ushort[span]; Span<ushort> values = stackalloc ushort[span];
for (int y = 0; y < tileHeight; y++) 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); ref uint inputRef = ref MemoryMarshal.GetReference(srcSpan);
for (nint x = 0; x <= tileWidth - span; x += span) for (nint x = 0; x <= tileWidth - span; x += span)
{ {
@ -181,19 +176,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int leftOver = tileWidth & (span - 1); int leftOver = tileWidth & (span - 1);
if (leftOver > 0) 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) else if (Sse41.IsSupported)
{ {
Vector128<byte> collectColorRedTransformsGreenMask = Vector128.Create(0x00ff00).AsByte(); Vector128<byte> collectColorRedTransformsGreenMask = Vector128.Create(0x00ff00).AsByte();
Vector128<byte> collectColorRedTransformsAndMask = Vector128.Create((short)0xff).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; const int span = 8;
Span<ushort> values = stackalloc ushort[span]; Span<ushort> values = stackalloc ushort[span];
for (int y = 0; y < tileHeight; y++) 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); ref uint inputRef = ref MemoryMarshal.GetReference(srcSpan);
for (nint x = 0; x <= tileWidth - span; x += span) for (nint x = 0; x <= tileWidth - span; x += span)
{ {
@ -224,11 +219,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int leftOver = tileWidth & (span - 1); int leftOver = tileWidth & (span - 1);
if (leftOver > 0) if (leftOver > 0)
{ {
CollectColorRedTransformsNoneVectorized(bgra.Slice(tileWidth - leftOver), stride, leftOver, tileHeight, greenToRed, histo); CollectColorRedTransformsNoneVectorized(bgra[(tileWidth - leftOver)..], stride, leftOver, tileHeight, greenToRed, histo);
} }
} }
else else
#endif
{ {
CollectColorRedTransformsNoneVectorized(bgra, stride, tileWidth, tileHeight, greenToRed, histo); CollectColorRedTransformsNoneVectorized(bgra, stride, tileWidth, tileHeight, greenToRed, histo);
} }

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

@ -4,11 +4,9 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Webp.Lossless namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
@ -68,9 +66,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
if (distance < PrefixLookupIdxMax) if (distance < PrefixLookupIdxMax)
{ {
(int Code, int ExtraBits) prefixCode = WebpLookupTables.PrefixEncodeCode[distance]; (int code, int bits) = WebpLookupTables.PrefixEncodeCode[distance];
extraBits = prefixCode.ExtraBits; extraBits = bits;
return prefixCode.Code; return code;
} }
return PrefixEncodeBitsNoLut(distance, ref extraBits); return PrefixEncodeBitsNoLut(distance, ref extraBits);
@ -80,11 +78,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
if (distance < PrefixLookupIdxMax) if (distance < PrefixLookupIdxMax)
{ {
(int Code, int ExtraBits) prefixCode = WebpLookupTables.PrefixEncodeCode[distance]; (int code, int bits) = WebpLookupTables.PrefixEncodeCode[distance];
extraBits = prefixCode.ExtraBits; extraBits = bits;
extraBitsValue = WebpLookupTables.PrefixEncodeExtraBitsValue[distance]; extraBitsValue = WebpLookupTables.PrefixEncodeExtraBitsValue[distance];
return prefixCode.Code; return code;
} }
return PrefixEncodeNoLut(distance, ref extraBits, ref extraBitsValue); 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> /// <param name="pixelData">The pixel data to apply the transformation.</param>
public static void AddGreenToBlueAndRed(Span<uint> pixelData) public static void AddGreenToBlueAndRed(Span<uint> pixelData)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) 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; int numPixels = pixelData.Length;
nint i; nint i;
for (i = 0; i <= numPixels - 8; i += 8) for (i = 0; i <= numPixels - 8; i += 8)
@ -113,12 +110,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (i != numPixels) if (i != numPixels)
{ {
AddGreenToBlueAndRedScalar(pixelData.Slice((int)i)); AddGreenToBlueAndRedScalar(pixelData[(int)i..]);
} }
} }
else if (Ssse3.IsSupported) 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; int numPixels = pixelData.Length;
nint i; nint i;
for (i = 0; i <= numPixels - 4; i += 4) for (i = 0; i <= numPixels - 4; i += 4)
@ -132,7 +129,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (i != numPixels) if (i != numPixels)
{ {
AddGreenToBlueAndRedScalar(pixelData.Slice((int)i)); AddGreenToBlueAndRedScalar(pixelData[(int)i..]);
} }
} }
else if (Sse2.IsSupported) else if (Sse2.IsSupported)
@ -154,11 +151,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (i != numPixels) if (i != numPixels)
{ {
AddGreenToBlueAndRedScalar(pixelData.Slice((int)i)); AddGreenToBlueAndRedScalar(pixelData[(int)i..]);
} }
} }
else else
#endif
{ {
AddGreenToBlueAndRedScalar(pixelData); AddGreenToBlueAndRedScalar(pixelData);
} }
@ -180,10 +176,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
public static void SubtractGreenFromBlueAndRed(Span<uint> pixelData) public static void SubtractGreenFromBlueAndRed(Span<uint> pixelData)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) 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; int numPixels = pixelData.Length;
nint i; nint i;
for (i = 0; i <= numPixels - 8; i += 8) for (i = 0; i <= numPixels - 8; i += 8)
@ -197,12 +192,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (i != numPixels) if (i != numPixels)
{ {
SubtractGreenFromBlueAndRedScalar(pixelData.Slice((int)i)); SubtractGreenFromBlueAndRedScalar(pixelData[(int)i..]);
} }
} }
else if (Ssse3.IsSupported) 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; int numPixels = pixelData.Length;
nint i; nint i;
for (i = 0; i <= numPixels - 4; i += 4) for (i = 0; i <= numPixels - 4; i += 4)
@ -216,7 +211,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (i != numPixels) if (i != numPixels)
{ {
SubtractGreenFromBlueAndRedScalar(pixelData.Slice((int)i)); SubtractGreenFromBlueAndRedScalar(pixelData[(int)i..]);
} }
} }
else if (Sse2.IsSupported) else if (Sse2.IsSupported)
@ -238,11 +233,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (i != numPixels) if (i != numPixels)
{ {
SubtractGreenFromBlueAndRedScalar(pixelData.Slice((int)i)); SubtractGreenFromBlueAndRedScalar(pixelData[(int)i..]);
} }
} }
else else
#endif
{ {
SubtractGreenFromBlueAndRedScalar(pixelData); SubtractGreenFromBlueAndRedScalar(pixelData);
} }
@ -375,11 +369,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <param name="numPixels">The number of pixels to process.</param> /// <param name="numPixels">The number of pixels to process.</param>
public static void TransformColor(Vp8LMultipliers m, Span<uint> pixelData, int numPixels) public static void TransformColor(Vp8LMultipliers m, Span<uint> pixelData, int numPixels)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && numPixels >= 8) 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); 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);
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 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> multsrb = MkCst32(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue));
Vector256<int> multsb2 = MkCst32(Cst5b(m.RedToBlue), 0); Vector256<int> multsb2 = MkCst32(Cst5b(m.RedToBlue), 0);
@ -405,13 +398,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (idx != numPixels) if (idx != numPixels)
{ {
TransformColorScalar(m, pixelData.Slice((int)idx), numPixels - (int)idx); TransformColorScalar(m, pixelData[(int)idx..], numPixels - (int)idx);
} }
} }
else if (Sse2.IsSupported) 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); var 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 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> multsrb = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue));
Vector128<int> multsb2 = MkCst16(Cst5b(m.RedToBlue), 0); Vector128<int> multsb2 = MkCst16(Cst5b(m.RedToBlue), 0);
nint idx; nint idx;
@ -436,11 +429,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (idx != numPixels) if (idx != numPixels)
{ {
TransformColorScalar(m, pixelData.Slice((int)idx), numPixels - (int)idx); TransformColorScalar(m, pixelData[(int)idx..], numPixels - (int)idx);
} }
} }
else else
#endif
{ {
TransformColorScalar(m, pixelData, numPixels); 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> /// <param name="pixelData">The pixel data to apply the inverse transform on.</param>
public static void TransformColorInverse(Vp8LMultipliers m, Span<uint> pixelData) public static void TransformColorInverse(Vp8LMultipliers m, Span<uint> pixelData)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && pixelData.Length >= 8) 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> multsrb = MkCst32(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue));
Vector256<int> multsb2 = MkCst32(Cst5b(m.RedToBlue), 0); Vector256<int> multsb2 = MkCst32(Cst5b(m.RedToBlue), 0);
nint idx; nint idx;
@ -500,12 +491,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (idx != pixelData.Length) if (idx != pixelData.Length)
{ {
TransformColorInverseScalar(m, pixelData.Slice((int)idx)); TransformColorInverseScalar(m, pixelData[(int)idx..]);
} }
} }
else if (Sse2.IsSupported) 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> multsrb = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue));
Vector128<int> multsb2 = MkCst16(Cst5b(m.RedToBlue), 0); Vector128<int> multsb2 = MkCst16(Cst5b(m.RedToBlue), 0);
@ -532,11 +523,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (idx != pixelData.Length) if (idx != pixelData.Length)
{ {
TransformColorInverseScalar(m, pixelData.Slice((int)idx)); TransformColorInverseScalar(m, pixelData[(int)idx..]);
} }
} }
else else
#endif
{ {
TransformColorInverseScalar(m, pixelData); TransformColorInverseScalar(m, pixelData);
} }
@ -753,7 +743,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <returns>Shanon entropy.</returns> /// <returns>Shanon entropy.</returns>
public static float CombinedShannonEntropy(Span<int> x, Span<int> y) public static float CombinedShannonEntropy(Span<int> x, Span<int> y)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
double retVal = 0.0d; double retVal = 0.0d;
@ -906,7 +895,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
return (float)retVal; return (float)retVal;
} }
else else
#endif
{ {
double retVal = 0.0d; double retVal = 0.0d;
uint sumX = 0, sumXY = 0; 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) private static uint ClampedAddSubtractFull(uint c0, uint c1, uint c2)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
Vector128<byte> c0Vec = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c0).AsByte(), Vector128<byte>.Zero); 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> v1 = Sse2.Add(c0Vec.AsInt16(), c1Vec.AsInt16());
Vector128<short> v2 = Sse2.Subtract(v1, c2Vec.AsInt16()); Vector128<short> v2 = Sse2.Subtract(v1, c2Vec.AsInt16());
Vector128<byte> b = Sse2.PackUnsignedSaturate(v2, v2); Vector128<byte> b = Sse2.PackUnsignedSaturate(v2, v2);
uint output = Sse2.ConvertToUInt32(b.AsUInt32()); return Sse2.ConvertToUInt32(b.AsUInt32());
return output;
} }
#endif
{ {
int a = AddSubtractComponentFull( int a = AddSubtractComponentFull(
(int)(c0 >> 24), (int)(c0 >> 24),
@ -1439,7 +1425,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static uint ClampedAddSubtractHalf(uint c0, uint c1, uint c2) private static uint ClampedAddSubtractHalf(uint c0, uint c1, uint c2)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
Vector128<byte> c0Vec = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c0).AsByte(), Vector128<byte>.Zero); 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> a3 = Sse2.ShiftRightArithmetic(a2, 1);
Vector128<short> a4 = Sse2.Add(a0, a3).AsInt16(); Vector128<short> a4 = Sse2.Add(a0, a3).AsInt16();
Vector128<byte> a5 = Sse2.PackUnsignedSaturate(a4, a4); Vector128<byte> a5 = Sse2.PackUnsignedSaturate(a4, a4);
uint output = Sse2.ConvertToUInt32(a5.AsUInt32()); return Sse2.ConvertToUInt32(a5.AsUInt32());
return output;
} }
#endif
{ {
uint ave = Average2(c0, c1); uint ave = Average2(c0, c1);
int a = AddSubtractComponentHalf((int)(ave >> 24), (int)(c2 >> 24)); int a = AddSubtractComponentHalf((int)(ave >> 24), (int)(c2 >> 24));
@ -1476,17 +1460,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint Clip255(uint a) => a < 256 ? a : ~a >> 24; private static uint Clip255(uint a) => a < 256 ? a : ~a >> 24;
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static Vector128<int> MkCst16(int hi, int lo) => Vector128.Create((hi << 16) | (lo & 0xffff)); private static Vector128<int> MkCst16(int hi, int lo) => Vector128.Create((hi << 16) | (lo & 0xffff));
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static Vector256<int> MkCst32(int hi, int lo) => Vector256.Create((hi << 16) | (lo & 0xffff)); 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) private static uint Select(uint a, uint b, uint c, Span<short> scratch)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
Span<short> output = scratch; Span<short> output = scratch;
@ -1510,7 +1491,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
} }
else else
#endif
{ {
int paMinusPb = int paMinusPb =
Sub3((int)(a >> 24), (int)(b >> 24), (int)(c >> 24)) + 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.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Formats.Webp.Lossless 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(b.Length, count, nameof(b.Length));
DebugGuard.MustBeGreaterThanOrEqualTo(output.Length, count, nameof(output.Length)); DebugGuard.MustBeGreaterThanOrEqualTo(output.Length, count, nameof(output.Length));
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
ref uint aRef = ref MemoryMarshal.GetReference(a); ref uint aRef = ref MemoryMarshal.GetReference(a);
@ -551,7 +548,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
} }
else else
#endif
{ {
for (int i = 0; i < count; i++) 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.Buffers.Binary;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Webp.Lossy namespace SixLabors.ImageSharp.Formats.Webp.Lossy
@ -19,7 +17,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8_Sse16X16(Span<byte> a, Span<byte> b) public static int Vp8_Sse16X16(Span<byte> a, Span<byte> b)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
return Vp8_Sse16xN_Avx2(a, b, 4); return Vp8_Sse16xN_Avx2(a, b, 4);
@ -29,17 +26,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
return Vp8_Sse16xN_Sse2(a, b, 8); 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. // Note: method name in libwebp reference implementation is called VP8SSE16x8.
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8_Sse16X8(Span<byte> a, Span<byte> b) public static int Vp8_Sse16X8(Span<byte> a, Span<byte> b)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
return Vp8_Sse16xN_Avx2(a, b, 2); return Vp8_Sse16xN_Avx2(a, b, 2);
@ -49,17 +43,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
return Vp8_Sse16xN_Sse2(a, b, 4); 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. // Note: method name in libwebp reference implementation is called VP8SSE4x4.
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8_Sse4X4(Span<byte> a, Span<byte> b) public static int Vp8_Sse4X4(Span<byte> a, Span<byte> b)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
// Load values. // Load values.
@ -128,10 +119,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return Numerics.ReduceSum(sum); return Numerics.ReduceSum(sum);
} }
#endif
{ return Vp8_SseNxN(a, b, 4, 4);
return Vp8_SseNxN(a, b, 4, 4);
}
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -155,7 +144,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return count; return count;
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int Vp8_Sse16xN_Sse2(Span<byte> a, Span<byte> b, int numPairs) 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); return Avx2.Add(sum1, sum2);
} }
#endif
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void Vp8Copy4X4(Span<byte> src, Span<byte> dst) => Copy(src, dst, 4, 4); 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) public static int Vp8Disto16X16(Span<byte> a, Span<byte> b, Span<ushort> w, Span<int> scratch)
{ {
int d = 0; 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 y = 0; y < 16 * WebpConstants.Bps; y += 4 * WebpConstants.Bps)
{ {
for (int x = 0; x < 16; x += 4) for (int x = 0; x < 16; x += 4)
@ -289,14 +276,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8Disto4X4(Span<byte> a, Span<byte> b, Span<ushort> w, Span<int> scratch) public static int Vp8Disto4X4(Span<byte> a, Span<byte> b, Span<ushort> w, Span<int> scratch)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse41.IsSupported) if (Sse41.IsSupported)
{ {
int diffSum = TTransformSse41(a, b, w); int diffSum = TTransformSse41(a, b, w);
return Math.Abs(diffSum) >> 5; return Math.Abs(diffSum) >> 5;
} }
else else
#endif
{ {
int sum1 = TTransform(a, w, scratch); int sum1 = TTransform(a, w, scratch);
int sum2 = TTransform(b, 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++) for (int j = 0; j < 16; j++)
{ {
// memcpy(dst + j * BPS, dst - BPS, 16); // 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]; byte v = yuv[offset];
Memset(dst, v, 0, 16); Memset(dst, v, 0, 16);
offset += WebpConstants.Bps; offset += WebpConstants.Bps;
dst = dst.Slice(WebpConstants.Bps); dst = dst[WebpConstants.Bps..];
} }
} }
@ -399,11 +384,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// vertical // vertical
Span<byte> src = yuv.Slice(offset - WebpConstants.Bps, 8); 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) for (int j = 0; j < endIdx; j += WebpConstants.Bps)
{ {
// memcpy(dst + j * BPS, dst - BPS, 8); // 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; // dst += BPS;
byte v = yuv[offset]; byte v = yuv[offset];
Memset(dst, v, 0, 8); Memset(dst, v, 0, 8);
dst = dst.Slice(WebpConstants.Bps); dst = dst[WebpConstants.Bps..];
offset += WebpConstants.Bps; offset += WebpConstants.Bps;
} }
} }
@ -427,7 +412,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// DC with no top samples. // DC with no top samples.
int dc0 = 4; int dc0 = 4;
int offsetMinusOne = offset - 1; int offsetMinusOne = offset - 1;
int endIdx = 8 * WebpConstants.Bps; const int endIdx = 8 * WebpConstants.Bps;
for (int i = 0; i < endIdx; i += WebpConstants.Bps) for (int i = 0; i < endIdx; i += WebpConstants.Bps)
{ {
// dc0 += dst[-1 + i * BPS]; // dc0 += dst[-1 + i * BPS];
@ -466,7 +451,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
dc >>= 3; dc >>= 3;
int endIndx = 4 * WebpConstants.Bps; const int endIndx = 4 * WebpConstants.Bps;
for (int i = 0; i < endIndx; i += WebpConstants.Bps) for (int i = 0; i < endIndx; i += WebpConstants.Bps)
{ {
Memset(dst, (byte)dc, i, 4); 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[1] = Avg3(yuv[topOffset], yuv[topOffset + 1], yuv[topOffset + 2]);
vals[2] = Avg3(yuv[topOffset + 1], yuv[topOffset + 2], yuv[topOffset + 3]); vals[2] = Avg3(yuv[topOffset + 1], yuv[topOffset + 2], yuv[topOffset + 3]);
vals[3] = Avg3(yuv[topOffset + 2], yuv[topOffset + 3], yuv[topOffset + 4]); 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) 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); uint val = 0x01010101U * Avg3(a, b, c);
BinaryPrimitives.WriteUInt32BigEndian(dst, val); BinaryPrimitives.WriteUInt32BigEndian(dst, val);
val = 0x01010101U * Avg3(b, c, d); 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); 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); 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) public static void RD4(Span<byte> dst, Span<byte> yuv, int offset)
@ -726,7 +711,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// </summary> /// </summary>
public static void TransformWht(Span<short> input, Span<short> output, Span<int> scratch) 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(); tmp.Clear();
for (int i = 0; i < 4; i++) 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) public static int TTransform(Span<byte> input, Span<ushort> w, Span<int> scratch)
{ {
int sum = 0; int sum = 0;
Span<int> tmp = scratch.Slice(0, 16); Span<int> tmp = scratch[..16];
tmp.Clear(); tmp.Clear();
// horizontal pass. // horizontal pass.
@ -807,13 +792,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
sum += w[8] * Math.Abs(b2); sum += w[8] * Math.Abs(b2);
sum += w[12] * Math.Abs(b3); sum += w[12] * Math.Abs(b3);
w = w.Slice(1); w = w[1..];
} }
return sum; return sum;
} }
#if SUPPORTS_RUNTIME_INTRINSICS
/// <summary> /// <summary>
/// Hadamard transform /// Hadamard transform
/// Returns the weighted sum of the absolute value of transformed coefficients. /// 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 // a02 a12 a22 a32 b02 b12 b22 b32
// a03 a13 a23 a33 b03 b13 b23 b33 // a03 a13 a23 a33 b03 b13 b23 b33
} }
#endif
// Transforms (Paragraph 14.4). // Transforms (Paragraph 14.4).
// Does two transforms. // Does two transforms.
public static void TransformTwo(Span<short> src, Span<byte> dst, Span<int> scratch) public static void TransformTwo(Span<short> src, Span<byte> dst, Span<int> scratch)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
// This implementation makes use of 16-bit fixed point versions of two // 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> a = Sse2.Add(in0.AsInt16(), in2.AsInt16());
Vector128<short> b = Sse2.Subtract(in0.AsInt16(), in2.AsInt16()); Vector128<short> b = Sse2.Subtract(in0.AsInt16(), in2.AsInt16());
Vector128<short> k1 = Vector128.Create((short)20091); var k1 = Vector128.Create((short)20091);
Vector128<short> k2 = Vector128.Create((short)-30068); var k2 = Vector128.Create((short)-30068);
// c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3 // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
Vector128<short> c1 = Sse2.MultiplyHigh(in1.AsInt16(), k2); 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(); Unsafe.As<byte, Vector64<byte>>(ref Unsafe.Add(ref outputRef, WebpConstants.Bps * 3)) = dst3.GetLower();
} }
else else
#endif
{ {
TransformOne(src, dst, scratch); 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) public static void TransformOne(Span<short> src, Span<byte> dst, Span<int> scratch)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
// Load and concatenate the transform coefficients. // 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> a = Sse2.Add(in0.AsInt16(), in2.AsInt16());
Vector128<short> b = Sse2.Subtract(in0.AsInt16(), in2.AsInt16()); Vector128<short> b = Sse2.Subtract(in0.AsInt16(), in2.AsInt16());
Vector128<short> k1 = Vector128.Create((short)20091); var k1 = Vector128.Create((short)20091);
Vector128<short> k2 = Vector128.Create((short)-30068); var k2 = Vector128.Create((short)-30068);
// c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3 // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
Vector128<short> c1 = Sse2.MultiplyHigh(in1.AsInt16(), k2); 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; Unsafe.As<byte, int>(ref Unsafe.Add(ref outputRef, WebpConstants.Bps * 3)) = output3;
} }
else else
#endif
{ {
Span<int> tmp = scratch.Slice(0, 16); Span<int> tmp = scratch[..16];
int tmpOffset = 0; int tmpOffset = 0;
for (int srcOffset = 0; srcOffset < 4; srcOffset++) for (int srcOffset = 0; srcOffset < 4; srcOffset++)
{ {
@ -1263,10 +1242,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int b = dc - tmp[tmpOffsetPlus8]; int b = dc - tmp[tmpOffsetPlus8];
int c = Mul2(tmp[tmpOffsetPlus4]) - Mul1(tmp[tmpOffsetPlus12]); int c = Mul2(tmp[tmpOffsetPlus4]) - Mul1(tmp[tmpOffsetPlus12]);
int d = Mul1(tmp[tmpOffsetPlus4]) + Mul2(tmp[tmpOffsetPlus12]); int d = Mul1(tmp[tmpOffsetPlus4]) + Mul2(tmp[tmpOffsetPlus12]);
Store(dst.Slice(dstOffset), 0, 0, a + d); Store(dst[dstOffset..], 0, 0, a + d);
Store(dst.Slice(dstOffset), 1, 0, b + c); Store(dst[dstOffset..], 1, 0, b + c);
Store(dst.Slice(dstOffset), 2, 0, b - c); Store(dst[dstOffset..], 2, 0, b - c);
Store(dst.Slice(dstOffset), 3, 0, a - d); Store(dst[dstOffset..], 3, 0, a - d);
tmpOffset++; tmpOffset++;
dstOffset += WebpConstants.Bps; 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) public static void TransformUv(Span<short> src, Span<byte> dst, Span<int> scratch)
{ {
TransformTwo(src.Slice(0 * 16), dst, scratch); TransformTwo(src[..], dst, scratch);
TransformTwo(src.Slice(2 * 16), dst.Slice(4 * WebpConstants.Bps), scratch); TransformTwo(src[(2 * 16)..], dst[(4 * WebpConstants.Bps)..], scratch);
} }
public static void TransformDcuv(Span<short> src, Span<byte> dst) public static void TransformDcuv(Span<short> src, Span<byte> dst)
{ {
if (src[0 * 16] != 0) if (src[0 * 16] != 0)
{ {
TransformDc(src.Slice(0 * 16), dst); TransformDc(src[..], dst);
} }
if (src[1 * 16] != 0) if (src[1 * 16] != 0)
{ {
TransformDc(src.Slice(1 * 16), dst.Slice(4)); TransformDc(src[(1 * 16)..], dst[4..]);
} }
if (src[2 * 16] != 0) 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) 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) // Simple In-loop filtering (Paragraph 15.2)
public static void SimpleVFilter16(Span<byte> p, int offset, int stride, int thresh) public static void SimpleVFilter16(Span<byte> p, int offset, int stride, int thresh)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
// Load. // Load.
@ -1351,7 +1329,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Unsafe.As<byte, Vector128<sbyte>>(ref outputRef) = q0.AsSByte(); Unsafe.As<byte, Vector128<sbyte>>(ref outputRef) = q0.AsSByte();
} }
else else
#endif
{ {
int thresh2 = (2 * thresh) + 1; int thresh2 = (2 * thresh) + 1;
int end = 16 + offset; 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) public static void SimpleHFilter16(Span<byte> p, int offset, int stride, int thresh)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
// Beginning of p1 // 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); Store16x4(p1, p0, q0, q1, ref pRef, ref Unsafe.Add(ref pRef, 8 * stride), stride);
} }
else else
#endif
{ {
int thresh2 = (2 * thresh) + 1; int thresh2 = (2 * thresh) + 1;
int end = offset + (16 * stride); 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) public static void SimpleVFilter16i(Span<byte> p, int offset, int stride, int thresh)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
for (int k = 3; k > 0; k--) for (int k = 3; k > 0; k--)
@ -1404,7 +1378,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
} }
else else
#endif
{ {
for (int k = 3; k > 0; k--) 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) public static void SimpleHFilter16i(Span<byte> p, int offset, int stride, int thresh)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
for (int k = 3; k > 0; k--) for (int k = 3; k > 0; k--)
@ -1426,7 +1398,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
} }
else else
#endif
{ {
for (int k = 3; k > 0; k--) for (int k = 3; k > 0; k--)
{ {
@ -1440,7 +1411,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void VFilter16(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh) public static void VFilter16(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
ref byte pRef = ref MemoryMarshal.GetReference(p); 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(); Unsafe.As<byte, Vector128<int>>(ref Unsafe.Add(ref outputRef, offset + (2 * stride))) = q2.AsInt32();
} }
else else
#endif
{ {
FilterLoop26(p, offset, stride, 1, 16, thresh, ithresh, hevThresh); FilterLoop26(p, offset, stride, 1, 16, thresh, ithresh, hevThresh);
} }
@ -1484,7 +1453,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void HFilter16(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh) public static void HFilter16(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
ref byte pRef = ref MemoryMarshal.GetReference(p); 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); Store16x4(q0, q1, q2, q3, ref Unsafe.Add(ref pRef, offset), ref Unsafe.Add(ref pRef, offset + (8 * stride)), stride);
} }
else else
#endif
{ {
FilterLoop26(p, offset, 1, stride, 16, thresh, ithresh, hevThresh); 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) public static void VFilter16i(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
ref byte pRef = ref MemoryMarshal.GetReference(p); ref byte pRef = ref MemoryMarshal.GetReference(p);
@ -1528,7 +1494,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int k = 3; k > 0; k--) for (int k = 3; k > 0; k--)
{ {
// Beginning of p1. // Beginning of p1.
Span<byte> b = p.Slice(offset + (2 * stride)); Span<byte> b = p[(offset + (2 * stride))..];
offset += 4 * stride; offset += 4 * stride;
Vector128<byte> mask = Abs(p0, p1); Vector128<byte> mask = Abs(p0, p1);
@ -1562,7 +1528,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
} }
else else
#endif
{ {
for (int k = 3; k > 0; k--) 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) public static void HFilter16i(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
ref byte pRef = ref MemoryMarshal.GetReference(p); ref byte pRef = ref MemoryMarshal.GetReference(p);
@ -1611,7 +1575,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
} }
else else
#endif
{ {
for (int k = 3; k > 0; k--) for (int k = 3; k > 0; k--)
{ {
@ -1625,7 +1588,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void VFilter8(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh) 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) if (Sse2.IsSupported)
{ {
// Load uv h-edges. // Load uv h-edges.
@ -1661,7 +1623,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
StoreUv(q2, ref uRef, ref vRef, offset + (2 * stride)); StoreUv(q2, ref uRef, ref vRef, offset + (2 * stride));
} }
else else
#endif
{ {
FilterLoop26(u, offset, stride, 1, 8, thresh, ithresh, hevThresh); FilterLoop26(u, offset, stride, 1, 8, thresh, ithresh, hevThresh);
FilterLoop26(v, 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)] [MethodImpl(InliningOptions.ShortMethod)]
public static void HFilter8(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh) 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) if (Sse2.IsSupported)
{ {
ref byte uRef = ref MemoryMarshal.GetReference(u); 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); Store16x4(q0, q1, q2, q3, ref Unsafe.Add(ref uRef, offset), ref Unsafe.Add(ref vRef, offset), stride);
} }
else else
#endif
{ {
FilterLoop26(u, offset, 1, stride, 8, thresh, ithresh, hevThresh); FilterLoop26(u, offset, 1, stride, 8, thresh, ithresh, hevThresh);
FilterLoop26(v, 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)] [MethodImpl(InliningOptions.ShortMethod)]
public static void VFilter8i(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh) 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) if (Sse2.IsSupported)
{ {
// Load uv h-edges. // Load uv h-edges.
@ -1741,7 +1699,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
StoreUv(q1, ref uRef, ref vRef, offset + stride); StoreUv(q1, ref uRef, ref vRef, offset + stride);
} }
else else
#endif
{ {
int offset4mulstride = offset + (4 * stride); int offset4mulstride = offset + (4 * stride);
FilterLoop24(u, offset4mulstride, stride, 1, 8, thresh, ithresh, hevThresh); FilterLoop24(u, offset4mulstride, stride, 1, 8, thresh, ithresh, hevThresh);
@ -1752,7 +1709,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void HFilter8i(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh) 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) if (Sse2.IsSupported)
{ {
ref byte uRef = ref MemoryMarshal.GetReference(u); 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); Store16x4(p1, p0, q0, q1, ref Unsafe.Add(ref uRef, offset), ref Unsafe.Add(ref vRef, offset), stride);
} }
else else
#endif
{ {
int offsetPlus4 = offset + 4; int offsetPlus4 = offset + 4;
FilterLoop24(u, offsetPlus4, 1, stride, 8, thresh, ithresh, hevThresh); 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) public static void Mean16x4(Span<byte> input, Span<uint> dc)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Ssse3.IsSupported) if (Ssse3.IsSupported)
{ {
Vector128<byte> mean16x4Mask = Vector128.Create((short)0x00ff).AsByte(); 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; Unsafe.As<uint, Vector128<uint>>(ref outputRef) = wide;
} }
else else
#endif
{ {
for (int k = 0; k < 4; k++) for (int k = 0; k < 4; k++)
{ {
@ -1835,7 +1788,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
dc[k] = avg; 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++) 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. // For information about how true motion works, see rfc6386, page 52. ff and section 20.14.
int topOffset = offset - WebpConstants.Bps; int topOffset = offset - WebpConstants.Bps;
Span<byte> top = yuv.Slice(topOffset); Span<byte> top = yuv[topOffset..];
byte p = yuv[topOffset - 1]; byte p = yuv[topOffset - 1];
int leftOffset = offset - 1; int leftOffset = offset - 1;
byte left = yuv[leftOffset]; byte left = yuv[leftOffset];
@ -1881,7 +1834,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
leftOffset += WebpConstants.Bps; leftOffset += WebpConstants.Bps;
left = yuv[leftOffset]; 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); p[offset] = WebpLookupTables.Clip1(q0 - a1);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
// Applies filter on 2 pixels (p0 and q0) // 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) 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). // Convert p1/q1 to byte (for GetBaseDelta).
Vector128<byte> p1s = Sse2.Xor(p1, signBit); Vector128<byte> p1s = Sse2.Xor(p1, signBit);
@ -1992,7 +1944,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Compute hev mask. // Compute hev mask.
Vector128<byte> notHev = GetNotHev(ref p1, ref p0, ref q0, ref q1, tresh); 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. // Convert to signed values.
p1 = Sse2.Xor(p1, signBit); 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); Vector128<byte> notHev = GetNotHev(ref p1, ref p0, ref q0, ref q1, tresh);
// Convert to signed values. // Convert to signed values.
Vector128<byte> signBit = Vector128.Create((byte)0x80); var signBit = Vector128.Create((byte)0x80);
p1 = Sse2.Xor(p1, signBit); p1 = Sse2.Xor(p1, signBit);
p0 = Sse2.Xor(p0, signBit); p0 = Sse2.Xor(p0, signBit);
q0 = Sse2.Xor(q0, 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> flow = Sse2.UnpackLow(Vector128<byte>.Zero, f);
Vector128<byte> fhigh = Sse2.UnpackHigh(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> f9Low = Sse2.MultiplyHigh(flow.AsInt16(), nine); // Filter (lo) * 9
Vector128<short> f9High = Sse2.MultiplyHigh(fhigh.AsInt16(), nine); // Filter (hi) * 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> a2Low = Sse2.Add(f9Low, sixtyThree); // Filter * 9 + 63
Vector128<short> a2High = Sse2.Add(f9High, 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 // not_hev <= t1 && not_hev <= t2
return Sse2.CompareEqual(tMaxH, Vector128<byte>.Zero); return Sse2.CompareEqual(tMaxH, Vector128<byte>.Zero);
} }
#endif
// Applies filter on 4 pixels (p1, p0, q0 and q1) // Applies filter on 4 pixels (p1, p0, q0 and q1)
private static void DoFilter4(Span<byte> p, int offset, int step) 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; 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) 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); 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). // 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) 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> a1Low = Sse2.ShiftRightArithmetic(a0Low, 7);
Vector128<short> a1High = Sse2.ShiftRightArithmetic(a0High, 7); Vector128<short> a1High = Sse2.ShiftRightArithmetic(a0High, 7);
Vector128<sbyte> delta = Sse2.PackSignedSaturate(a1Low, a1High); Vector128<sbyte> delta = Sse2.PackSignedSaturate(a1Low, a1High);
@ -2374,7 +2324,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static Vector128<byte> Abs(Vector128<byte> p, Vector128<byte> q) private static Vector128<byte> Abs(Vector128<byte> p, Vector128<byte> q)
=> Sse2.Or(Sse2.SubtractSaturate(q, p), Sse2.SubtractSaturate(p, q)); => Sse2.Or(Sse2.SubtractSaturate(q, p), Sse2.SubtractSaturate(p, q));
#endif
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static bool Hev(Span<byte> p, int offset, int step, int thresh) 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)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Put8x8uv(byte value, Span<byte> dst) 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) for (int j = 0; j < end; j += WebpConstants.Bps)
{ {
// memset(dst + j * BPS, value, 8); // memset(dst + j * BPS, value, 8);

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

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

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

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

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

@ -4,10 +4,8 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Formats.Webp.Lossy namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
@ -46,7 +44,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public void SetCoeffs(Span<short> coeffs) public void SetCoeffs(Span<short> coeffs)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported) if (Sse2.IsSupported)
{ {
ref short coeffsRef = ref MemoryMarshal.GetReference(coeffs); 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; this.Last = mask != 0 ? Numerics.Log2(mask) : -1;
} }
else else
#endif
{ {
int n; int n;
this.Last = -1; 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. // Simulate block coding, but only record statistics.
@ -93,23 +89,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Vp8StatsArray s = this.Stats[n].Stats[ctx]; Vp8StatsArray s = this.Stats[n].Stats[ctx];
if (this.Last < 0) if (this.Last < 0)
{ {
this.RecordStats(0, s, 0); RecordStats(0, s, 0);
return 0; return 0;
} }
while (n <= this.Last) while (n <= this.Last)
{ {
int v; 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) while ((v = this.Coeffs[n++]) == 0)
{ {
this.RecordStats(0, s, 1); RecordStats(0, s, 1);
s = this.Stats[WebpConstants.Vp8EncBands[n]].Stats[0]; s = this.Stats[WebpConstants.Vp8EncBands[n]].Stats[0];
} }
this.RecordStats(1, s, 1); RecordStats(1, s, 1);
bool bit = (uint)(v + 1) > 2u; 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 // v = -1 or 1
s = this.Stats[WebpConstants.Vp8EncBands[n]].Stats[1]; s = this.Stats[WebpConstants.Vp8EncBands[n]].Stats[1];
@ -130,7 +126,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int mask = 2 << i; int mask = 2 << i;
if ((pattern & 1) != 0) 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) if (n < 16)
{ {
this.RecordStats(0, s, 0); RecordStats(0, s, 0);
} }
return 1; return 1;
@ -163,7 +159,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return LossyUtils.Vp8BitCost(0, (byte)p0); return LossyUtils.Vp8BitCost(0, (byte)p0);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
Span<byte> ctxs = this.scratch.AsSpan(0, 16); Span<byte> ctxs = this.scratch.AsSpan(0, 16);
@ -211,7 +206,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return cost; return cost;
} }
#endif
{ {
int v; int v;
for (; n < this.Last; ++n) for (; n < this.Last; ++n)
@ -241,7 +236,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private static int LevelCost(Span<ushort> table, int level) private static int LevelCost(Span<ushort> table, int level)
=> WebpLookupTables.Vp8LevelFixedCosts[level] + table[level > WebpConstants.MaxVariableLevel ? WebpConstants.MaxVariableLevel : 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 // An overflow is inbound. Note we handle this at 0xfffe0000u instead of
// 0xffff0000u to make sure p + 1u does not overflow. // 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.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; 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 // ([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) 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) if (Sse41.IsSupported)
{ {
UpSampleSse41(topY, bottomY, topU, topV, curU, curV, topDst, bottomDst, len, uvBuffer); UpSampleSse41(topY, bottomY, topU, topV, curU, curV, topDst, bottomDst, len, uvBuffer);
} }
else else
#endif
{ {
UpSampleScalar(topY, bottomY, topU, topV, curU, curV, topDst, bottomDst, len); 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) 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; int lastPixelPair = (len - 1) >> 1;
uint tluv = LoadUv(topU[0], topV[0]); // top-left sample uint tluv = LoadUv(topU[0], topV[0]); // top-left sample
uint luv = LoadUv(curU[0], curV[0]); // 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; uv0 = (diag12 + tluv) >> 1;
uint uv1 = (diag03 + tuv) >> 1; uint uv1 = (diag03 + tuv) >> 1;
int xMul2 = x * 2; int xMul2 = x * 2;
YuvToBgr(topY[xMul2 - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst.Slice((xMul2 - 1) * 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.Slice((xMul2 - 0) * xStep)); YuvToBgr(topY[xMul2 - 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), topDst[((xMul2 - 0) * xStep)..]);
if (bottomY != default) if (bottomY != default)
{ {
uv0 = (diag03 + luv) >> 1; uv0 = (diag03 + luv) >> 1;
uv1 = (diag12 + uv) >> 1; uv1 = (diag12 + uv) >> 1;
YuvToBgr(bottomY[xMul2 - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), bottomDst.Slice((xMul2 - 1) * 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.Slice((xMul2 + 0) * xStep)); YuvToBgr(bottomY[xMul2 + 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), bottomDst[((xMul2 + 0) * xStep)..]);
} }
tluv = tuv; tluv = tuv;
@ -89,16 +85,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if ((len & 1) == 0) if ((len & 1) == 0)
{ {
uv0 = ((3 * tluv) + luv + 0x00020002u) >> 2; 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) if (bottomY != default)
{ {
uv0 = ((3 * luv) + tluv + 0x00020002u) >> 2; 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 // We compute (9*a + 3*b + 3*c + d + 8) / 16 as follows
// u = (9*a + 3*b + 3*c + d + 8) / 16 // u = (9*a + 3*b + 3*c + d + 8) / 16
// = (a + (a + 3*b + 3*c + d) / 8 + 1) / 2 // = (a + (a + 3*b + 3*c + d) / 8 + 1) / 2
@ -118,7 +113,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
const int xStep = 3; const int xStep = 3;
Array.Clear(uvBuffer); Array.Clear(uvBuffer);
Span<byte> ru = uvBuffer.AsSpan(15); Span<byte> ru = uvBuffer.AsSpan(15);
Span<byte> rv = ru.Slice(32); Span<byte> rv = ru[32..];
// Treat the first pixel in regular way. // Treat the first pixel in regular way.
int uDiag = ((topU[0] + curU[0]) >> 1) + 1; 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 topVRef = ref MemoryMarshal.GetReference(topV);
ref byte curURef = ref MemoryMarshal.GetReference(curU); ref byte curURef = ref MemoryMarshal.GetReference(curU);
ref byte curVRef = ref MemoryMarshal.GetReference(curV); 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) 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) if (len > 1)
{ {
int leftOver = ((len + 1) >> 1) - (pos >> 1); int leftOver = ((len + 1) >> 1) - (pos >> 1);
Span<byte> tmpTopDst = ru.Slice(4 * 32); Span<byte> tmpTopDst = ru[(4 * 32)..];
Span<byte> tmpBottomDst = tmpTopDst.Slice(4 * 32); Span<byte> tmpBottomDst = tmpTopDst[(4 * 32)..];
Span<byte> tmpTop = tmpBottomDst.Slice(4 * 32); Span<byte> tmpTop = tmpBottomDst[(4 * 32)..];
Span<byte> tmpBottom = (bottomY == null) ? null : tmpTop.Slice(32); Span<byte> tmpBottom = (bottomY == default) ? null : tmpTop[32..];
UpSampleLastBlock(topU.Slice(uvPos), curU.Slice(uvPos), leftOver, ru); UpSampleLastBlock(topU[uvPos..], curU[uvPos..], leftOver, ru);
UpSampleLastBlock(topV.Slice(uvPos), curV.Slice(uvPos), leftOver, rv); UpSampleLastBlock(topV[uvPos..], curV[uvPos..], leftOver, rv);
topY.Slice(pos, len - pos).CopyTo(tmpTop); topY[pos..len].CopyTo(tmpTop);
if (bottomY != default) if (bottomY != default)
{ {
bottomY.Slice(pos, len - pos).CopyTo(tmpBottom); bottomY[pos..len].CopyTo(tmpBottom);
ConvertYuvToBgrWithBottomYSse41(tmpTop, tmpBottom, tmpTopDst, tmpBottomDst, ru, rv, 0, xStep); ConvertYuvToBgrWithBottomYSse41(tmpTop, tmpBottom, tmpTopDst, tmpBottomDst, ru, rv, 0, xStep);
} }
else else
@ -181,10 +176,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
ConvertYuvToBgrSse41(tmpTop, tmpTopDst, ru, rv, 0, xStep); 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) 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. // Pack the alternate pixels.
PackAndStore(a, b, diag1, diag2, output); // store top. 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) private static void UpSampleLastBlock(Span<byte> tb, Span<byte> bb, int numPixels, Span<byte> output)
{ {
Span<byte> r1 = stackalloc byte[17]; Span<byte> r1 = stackalloc byte[17];
Span<byte> r2 = stackalloc byte[17]; Span<byte> r2 = stackalloc byte[17];
tb.Slice(0, numPixels).CopyTo(r1); tb[..numPixels].CopyTo(r1);
bb.Slice(0, numPixels).CopyTo(r2); bb[..numPixels].CopyTo(r2);
// Replicate last byte. // Replicate last byte.
int length = 17 - numPixels; 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 output0Ref) = t1;
Unsafe.As<byte, Vector128<byte>>(ref output1Ref) = t2; Unsafe.As<byte, Vector128<byte>>(ref output1Ref) = t2;
} }
#endif
/// <summary> /// <summary>
/// Converts the RGB values of the image to YUV. /// Converts the RGB values of the image to YUV.
@ -317,11 +311,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
AccumulateRgba(bgraRow0, bgraRow1, tmpRgbSpan, width); 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++; uvRowIndex++;
ConvertRgbaToY(bgraRow0, y.Slice(rowIndex * width), width); ConvertRgbaToY(bgraRow0, y[(rowIndex * width)..], width);
ConvertRgbaToY(bgraRow1, y.Slice((rowIndex + 1) * width), width); ConvertRgbaToY(bgraRow1, y[((rowIndex + 1) * width)..], width);
} }
// Extra last row. // Extra last row.
@ -329,7 +323,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(rowIndex); Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(rowIndex);
PixelOperations<TPixel>.Instance.ToBgra32(configuration, rowSpan, bgraRow0); PixelOperations<TPixel>.Instance.ToBgra32(configuration, rowSpan, bgraRow0);
ConvertRgbaToY(bgraRow0, y.Slice(rowIndex * width), width); ConvertRgbaToY(bgraRow0, y[(rowIndex * width)..], width);
if (!WebpCommonUtils.CheckNonOpaque(bgraRow0)) if (!WebpCommonUtils.CheckNonOpaque(bgraRow0))
{ {
@ -341,7 +335,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
hasAlpha = true; hasAlpha = true;
} }
ConvertRgbaToUv(tmpRgbSpan, u.Slice(uvRowIndex * uvWidth), v.Slice(uvRowIndex * uvWidth), uvWidth); ConvertRgbaToUv(tmpRgbSpan, u[(uvRowIndex * uvWidth)..], v[(uvRowIndex * uvWidth)..], uvWidth);
} }
return hasAlpha; return hasAlpha;
@ -569,16 +563,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
bgr[0] = (byte)YuvToB(y, u); bgr[0] = (byte)YuvToB(y, u);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(InliningOptions.ShortMethod)] [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)] [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) 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(topY[curX..], ru, rv, topDst[(curX * step)..]);
YuvToBgrSse41(bottomY.Slice(curX), ru.Slice(64), rv.Slice(64), bottomDst.Slice(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) 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 // R = (19077 * y + 26149 * v - 14234) >> 6
// G = (19077 * y - 6419 * u - 13320 * v + 8708) >> 6 // G = (19077 * y - 6419 * u - 13320 * v + 8708) >> 6
// B = (19077 * y + 33050 * u - 17685) >> 6 // B = (19077 * y + 33050 * u - 17685) >> 6
Vector128<ushort> k19077 = Vector128.Create((ushort)19077); var k19077 = Vector128.Create((ushort)19077);
Vector128<ushort> k26149 = Vector128.Create((ushort)26149); var k26149 = Vector128.Create((ushort)26149);
Vector128<ushort> k14234 = Vector128.Create((ushort)14234); var k14234 = Vector128.Create((ushort)14234);
Vector128<ushort> y1 = Sse2.MultiplyHigh(y0.AsUInt16(), k19077); Vector128<ushort> y1 = Sse2.MultiplyHigh(y0.AsUInt16(), k19077);
Vector128<ushort> r0 = Sse2.MultiplyHigh(v0.AsUInt16(), k26149); 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] b = Sse2.ShiftRightLogical(b2.AsInt16(), 6); // range: [0, 34238]
} }
#endif
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static int YuvToB(int y, int u) => Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685); 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)] [MethodImpl(InliningOptions.ShortMethod)]
private static byte Clip8(int v) 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); 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;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Webp namespace SixLabors.ImageSharp.Formats.Webp
{ {
@ -23,7 +21,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// <returns>Returns true if alpha has non-0xff values.</returns> /// <returns>Returns true if alpha has non-0xff values.</returns>
public static unsafe bool CheckNonOpaque(Span<Bgra32> row) public static unsafe bool CheckNonOpaque(Span<Bgra32> row)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported) if (Avx2.IsSupported)
{ {
ReadOnlySpan<byte> rowBytes = MemoryMarshal.AsBytes(row); ReadOnlySpan<byte> rowBytes = MemoryMarshal.AsBytes(row);
@ -31,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
int length = (row.Length * 4) - 3; int length = (row.Length * 4) - 3;
fixed (byte* src = rowBytes) 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(); Vector256<byte> all0x80Vector256 = Vector256.Create((byte)0x80).AsByte();
for (; i + 128 <= length; i += 128) for (; i + 128 <= length; i += 128)
@ -113,7 +110,6 @@ namespace SixLabors.ImageSharp.Formats.Webp
} }
} }
else else
#endif
{ {
for (int x = 0; x < row.Length; x++) for (int x = 0; x < row.Length; x++)
{ {
@ -127,10 +123,9 @@ namespace SixLabors.ImageSharp.Formats.Webp
return false; return false;
} }
#if SUPPORTS_RUNTIME_INTRINSICS
private static unsafe bool IsNoneOpaque64Bytes(byte* src, int i) 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> a0 = Sse2.LoadVector128(src + i).AsByte();
Vector128<byte> a1 = Sse2.LoadVector128(src + i + 16).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> d = Sse2.PackSignedSaturate(c0, c1).AsByte();
Vector128<byte> bits = Sse2.CompareEqual(d, Vector128.Create((byte)0x80).AsByte()); Vector128<byte> bits = Sse2.CompareEqual(d, Vector128.Create((byte)0x80).AsByte());
int mask = Sse2.MoveMask(bits); int mask = Sse2.MoveMask(bits);
if (mask != 0xFFFF) return mask != 0xFFFF;
{
return true;
}
return false;
} }
private static unsafe bool IsNoneOpaque32Bytes(byte* src, int i) 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> a0 = Sse2.LoadVector128(src + i).AsByte();
Vector128<byte> a1 = Sse2.LoadVector128(src + i + 16).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> d = Sse2.PackSignedSaturate(c, c).AsByte();
Vector128<byte> bits = Sse2.CompareEqual(d, Vector128.Create((byte)0x80).AsByte()); Vector128<byte> bits = Sse2.CompareEqual(d, Vector128.Create((byte)0x80).AsByte());
int mask = Sse2.MoveMask(bits); int mask = Sse2.MoveMask(bits);
if (mask != 0xFFFF) return mask != 0xFFFF;
{
return true;
}
return false;
} }
#endif
} }
} }

4
src/ImageSharp/IO/BufferedReadStream.cs

@ -175,7 +175,6 @@ namespace SixLabors.ImageSharp.IO
return this.ReadToBufferViaCopyFast(buffer, offset, count); return this.ReadToBufferViaCopyFast(buffer, offset, count);
} }
#if SUPPORTS_SPAN_STREAM
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int Read(Span<byte> buffer) public override int Read(Span<byte> buffer)
@ -196,7 +195,6 @@ namespace SixLabors.ImageSharp.IO
return this.ReadToBufferViaCopyFast(buffer); return this.ReadToBufferViaCopyFast(buffer);
} }
#endif
/// <inheritdoc/> /// <inheritdoc/>
public override void Flush() public override void Flush()
@ -354,7 +352,7 @@ namespace SixLabors.ImageSharp.IO
int i; int i;
do do
{ {
i = baseStream.Read(buffer.Slice(n, count - n)); i = baseStream.Read(buffer[n..count]);
n += i; n += i;
} }
while (n < count && i > 0); 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)); return this.ReadImpl(buffer.AsSpan(offset, count));
} }
#if SUPPORTS_SPAN_STREAM
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int Read(Span<byte> buffer) => this.ReadImpl(buffer); public override int Read(Span<byte> buffer) => this.ReadImpl(buffer);
#endif
private int ReadImpl(Span<byte> buffer) private int ReadImpl(Span<byte> buffer)
{ {
@ -352,11 +350,9 @@ namespace SixLabors.ImageSharp.IO
this.WriteImpl(buffer.AsSpan(offset, count)); this.WriteImpl(buffer.AsSpan(offset, count));
} }
#if SUPPORTS_SPAN_STREAM
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Write(ReadOnlySpan<byte> buffer) => this.WriteImpl(buffer); public override void Write(ReadOnlySpan<byte> buffer) => this.WriteImpl(buffer);
#endif
private void WriteImpl(ReadOnlySpan<byte> buffer) 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.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using SixLabors.ImageSharp.Memory.Internals; using SixLabors.ImageSharp.Memory.Internals;
namespace SixLabors.ImageSharp.Memory namespace SixLabors.ImageSharp.Memory
@ -129,7 +128,7 @@ namespace SixLabors.ImageSharp.Memory
if (bufferCount > 0) 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); 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!"); 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); 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!"); 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); return new Owned(source, bufferLength, totalLength, false);
} }
@ -252,15 +251,10 @@ namespace SixLabors.ImageSharp.Memory
{ {
case SpanCacheMode.SingleArray: case SpanCacheMode.SingleArray:
{ {
#if SUPPORTS_CREATESPAN
ref byte b0 = ref MemoryMarshal.GetReference<byte>(this.memoryGroupSpanCache.SingleArray); ref byte b0 = ref MemoryMarshal.GetReference<byte>(this.memoryGroupSpanCache.SingleArray);
ref T e0 = ref Unsafe.As<byte, T>(ref b0); ref T e0 = ref Unsafe.As<byte, T>(ref b0);
e0 = ref Unsafe.Add(ref e0, y * width); e0 = ref Unsafe.Add(ref e0, y * width);
return MemoryMarshal.CreateSpan(ref e0, width); return MemoryMarshal.CreateSpan(ref e0, width);
#else
return MemoryMarshal.Cast<byte, T>(this.memoryGroupSpanCache.SingleArray).Slice(y * width, width);
#endif
} }
case SpanCacheMode.SinglePointer: 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> internal partial class PixelOperations : PixelOperations<Rgb24>
{ {
private static readonly Lazy<PixelTypeInfo> LazyInfo = private static readonly Lazy<PixelTypeInfo> LazyInfo =
new Lazy<PixelTypeInfo>(() => PixelTypeInfo.Create<Rgb24>(PixelAlphaRepresentation.None), true); new(() => PixelTypeInfo.Create<Rgb24>(PixelAlphaRepresentation.None), true);
/// <inheritdoc /> /// <inheritdoc />
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value;
@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.PixelFormats
int count = redChannel.Length; int count = redChannel.Length;
GuardPackFromRgbPlanes(greenChannel, blueChannel, destination, count); 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> internal partial class PixelOperations : PixelOperations<Rgba32>
{ {
private static readonly Lazy<PixelTypeInfo> LazyInfo = private static readonly Lazy<PixelTypeInfo> LazyInfo =
new Lazy<PixelTypeInfo>(() => PixelTypeInfo.Create<Rgba32>(PixelAlphaRepresentation.Unassociated), true); new(() => PixelTypeInfo.Create<Rgba32>(PixelAlphaRepresentation.Unassociated), true);
/// <inheritdoc /> /// <inheritdoc />
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value;
@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationVectors, nameof(destinationVectors)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationVectors, nameof(destinationVectors));
destinationVectors = destinationVectors.Slice(0, sourcePixels.Length); destinationVectors = destinationVectors[..sourcePixels.Length];
SimdUtils.ByteToNormalizedFloat( SimdUtils.ByteToNormalizedFloat(
MemoryMarshal.Cast<Rgba32, byte>(sourcePixels), MemoryMarshal.Cast<Rgba32, byte>(sourcePixels),
MemoryMarshal.Cast<Vector4, float>(destinationVectors)); MemoryMarshal.Cast<Vector4, float>(destinationVectors));
@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationPixels, nameof(destinationPixels)); Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationPixels, nameof(destinationPixels));
destinationPixels = destinationPixels.Slice(0, sourceVectors.Length); destinationPixels = destinationPixels[..sourceVectors.Length];
Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers); Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers);
SimdUtils.NormalizedFloatToByteSaturate( SimdUtils.NormalizedFloatToByteSaturate(
MemoryMarshal.Cast<Vector4, float>(sourceVectors), MemoryMarshal.Cast<Vector4, float>(sourceVectors),
@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.PixelFormats
int count = redChannel.Length; int count = redChannel.Length;
GuardPackFromRgbPlanes(greenChannel, blueChannel, destination, count); 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;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
namespace SixLabors.ImageSharp.Processing.Processors.Transforms namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
@ -56,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public Span<float> Values public Span<float> Values
{ {
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
get => new Span<float>(this.bufferPtr, this.Length); get => new(this.bufferPtr, this.Length);
} }
/// <summary> /// <summary>
@ -71,7 +69,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Vector4 ConvolveCore(ref Vector4 rowStartRef) public Vector4 ConvolveCore(ref Vector4 rowStartRef)
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && Fma.IsSupported) if (Avx2.IsSupported && Fma.IsSupported)
{ {
float* bufferStart = this.bufferPtr; float* bufferStart = this.bufferPtr;
@ -141,7 +138,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return *(Vector4*)&result128; return *(Vector4*)&result128;
} }
else else
#endif
{ {
// Destination color components // Destination color components
Vector4 result = Vector4.Zero; Vector4 result = Vector4.Zero;
@ -167,7 +163,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary> /// </summary>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
internal ResizeKernel AlterLeftValue(int left) internal ResizeKernel AlterLeftValue(int left)
=> new ResizeKernel(left, this.bufferPtr, this.Length); => new(left, this.bufferPtr, this.Length);
internal void Fill(Span<double> values) 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.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components; 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; Unsafe.As<float, Vector<float>>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 7))) = v3;
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark] [Benchmark]
public void UseVector256_Avx2_Variant1() public void UseVector256_Avx2_Variant1()
{ {
@ -319,7 +316,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{ {
fixed (Block8x8F* ss = &this.block) fixed (Block8x8F* ss = &this.block)
{ {
var s = (float*)ss; float* s = (float*)ss;
Vector256<float> v0 = Avx.LoadVector256(s); Vector256<float> v0 = Avx.LoadVector256(s);
Vector256<float> v1 = Avx.LoadVector256(s + 8); Vector256<float> v1 = Avx.LoadVector256(s + 8);
Vector256<float> v2 = Avx.LoadVector256(s + (8 * 2)); 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() public void UseVector256_Avx2_Variant3_sbyte()
{ {
int stride = Width * 4; int stride = Width * 4;
var d = (sbyte*)this.bufferPtr; sbyte* d = (sbyte*)this.bufferPtr;
var s = (sbyte*)this.blockPtr; sbyte* s = (sbyte*)this.blockPtr;
Vector256<sbyte> v0 = Avx.LoadVector256(s); Vector256<sbyte> v0 = Avx.LoadVector256(s);
Vector256<sbyte> v1 = Avx.LoadVector256(s + 32); 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 * 6), v2);
Avx.Store(d + (stride * 7), v3); Avx.Store(d + (stride * 7), v3);
} }
#endif
// *** RESULTS 02/2020 *** // *** RESULTS 02/2020 ***
// BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363 // 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.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
@ -136,7 +134,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
row3 = SimdUtils.FastRound(row3); row3 = SimdUtils.FastRound(row3);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark] [Benchmark]
public void Sse41_V1() public void Sse41_V1()
{ {
@ -491,6 +488,5 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
Sse.StoreAligned(p6, v6); Sse.StoreAligned(p6, v6);
Sse.StoreAligned(p7, v7); 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); new JpegColorConverterBase.FromCmykVector(8).ConvertToRgbInplace(values);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark] [Benchmark]
public void SimdVectorAvx() public void SimdVectorAvx()
{ {
@ -38,6 +37,5 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromCmykAvx(8).ConvertToRgbInplace(values); 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); new JpegColorConverterBase.FromGrayscaleScalar(8).ConvertToRgbInplace(values);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark] [Benchmark]
public void SimdVectorAvx() public void SimdVectorAvx()
{ {
@ -30,6 +29,5 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromGrayscaleAvx(8).ConvertToRgbInplace(values); 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); new JpegColorConverterBase.FromRgbVector(8).ConvertToRgbInplace(values);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark] [Benchmark]
public void SimdVectorAvx() public void SimdVectorAvx()
{ {
@ -38,6 +37,5 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromRgbAvx(8).ConvertToRgbInplace(values); 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); new JpegColorConverterBase.FromYCbCrVector(8).ConvertToRgbInplace(values);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark] [Benchmark]
public void SimdVectorAvx() public void SimdVectorAvx()
{ {
@ -38,6 +37,5 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromYCbCrAvx(8).ConvertToRgbInplace(values); 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); new JpegColorConverterBase.FromYccKVector(8).ConvertToRgbInplace(values);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark] [Benchmark]
public void SimdVectorAvx2() public void SimdVectorAvx2()
{ {
@ -38,6 +37,5 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
new JpegColorConverterBase.FromYccKAvx(8).ConvertToRgbInplace(values); 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.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -80,15 +78,6 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
SimdUtils.FallbackIntrinsics128.NormalizedFloatToByteSaturate(sBytes, dFloats); 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] [Benchmark]
public void ExtendedIntrinsic() public void ExtendedIntrinsic()
{ {
@ -98,7 +87,6 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
SimdUtils.ExtendedIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats); SimdUtils.ExtendedIntrinsics.NormalizedFloatToByteSaturate(sBytes, dFloats);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark] [Benchmark]
public void UseHwIntrinsics() public void UseHwIntrinsics()
{ {
@ -161,7 +149,6 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
vf = Avx.Multiply(scale, vf); vf = Avx.Multiply(scale, vf);
return Avx.ConvertToVector256Int32(vf); return Avx.ConvertToVector256Int32(vf);
} }
#endif
// *** RESULTS 2020 March: *** // *** RESULTS 2020 March: ***
// Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores // 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] [Benchmark]
public void PixelOperations_Base() public void PixelOperations_Base()
{ => new PixelOperations<Rgba32>().ToVector4(
new PixelOperations<Rgba32>().ToVector4(
this.Configuration, this.Configuration,
this.source.GetSpan(), this.source.GetSpan(),
this.destination.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] [Benchmark]
public void ExtendedIntrinsics() public void ExtendedIntrinsics()
@ -51,7 +40,6 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
SimdUtils.ExtendedIntrinsics.ByteToNormalizedFloat(sBytes, dFloats); SimdUtils.ExtendedIntrinsics.ByteToNormalizedFloat(sBytes, dFloats);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark] [Benchmark]
public void HwIntrinsics() public void HwIntrinsics()
{ {
@ -60,7 +48,6 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk
SimdUtils.HwIntrinsics.ByteToNormalizedFloat(sBytes, dFloats); SimdUtils.HwIntrinsics.ByteToNormalizedFloat(sBytes, dFloats);
} }
#endif
// [Benchmark] // [Benchmark]
public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_2Loops() 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); ref Vector<float> dRef = ref Unsafe.Add(ref destBase, i);
Vector<int> du = Vector.AsVectorInt32(dRef); var du = Vector.AsVectorInt32(dRef);
Vector<float> v = Vector.ConvertToSingle(du); var v = Vector.ConvertToSingle(du);
v *= scale; v *= scale;
dRef = v; dRef = v;

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

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

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

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

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

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

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

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

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

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

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

@ -1,22 +1,10 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Six Labors Split License.
using System; 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;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; 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;
using Xunit.Abstractions;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Jpg namespace SixLabors.ImageSharp.Tests.Formats.Jpg

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

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

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

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

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

@ -147,7 +147,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
[Fact] [Fact]
public void PaethFilter_Works() => RunPaethFilterTest(); public void PaethFilter_Works() => RunPaethFilterTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void AverageFilter_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAverageFilterTest, HwIntrinsics.AllowAll); public void AverageFilter_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAverageFilterTest, HwIntrinsics.AllowAll);
@ -174,6 +173,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
[Fact] [Fact]
public void PaethFilter_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.DisableHWIntrinsic); 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 0b10010000
}; };
private static readonly Rgba32[][] BilevelResult4X4 = { private static readonly Rgba32[][] BilevelResult4X4 =
{
new[] { Bit0, Bit1, Bit0, Bit1 }, new[] { Bit0, Bit1, Bit0, Bit1 },
new[] { Bit1, Bit1, Bit1, Bit1 }, new[] { Bit1, Bit1, Bit1, Bit1 },
new[] { Bit0, 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(Grayscale4_Data))]
[MemberData(nameof(Grayscale8_Data))] [MemberData(nameof(Grayscale8_Data))]
public void Decode_WritesPixelData(byte[] inputData, ushort bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) public void Decode_WritesPixelData(byte[] inputData, ushort bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult)
{ => AssertDecode(
AssertDecode(expectedResult, pixels => expectedResult,
{ pixels => new BlackIsZeroTiffColor<Rgba32>(new TiffBitsPerSample(bitsPerSample, 0, 0)).Decode(inputData, pixels, left, top, width, height));
new BlackIsZeroTiffColor<Rgba32>(new TiffBitsPerSample(bitsPerSample, 0, 0)).Decode(inputData, pixels, left, top, width, height);
});
}
[Theory] [Theory]
[MemberData(nameof(BilevelData))] [MemberData(nameof(BilevelData))]
public void Decode_WritesPixelData_Bilevel(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) 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] [Theory]
[MemberData(nameof(Grayscale4_Data))] [MemberData(nameof(Grayscale4_Data))]
public void Decode_WritesPixelData_4Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) public void Decode_WritesPixelData_4Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult)
{ => AssertDecode(
AssertDecode(expectedResult, pixels => expectedResult,
{ pixels => new BlackIsZero4TiffColor<Rgba32>().Decode(inputData, pixels, left, top, width, height));
new BlackIsZero4TiffColor<Rgba32>().Decode(inputData, pixels, left, top, width, height);
});
}
[Theory] [Theory]
[MemberData(nameof(Grayscale8_Data))] [MemberData(nameof(Grayscale8_Data))]
public void Decode_WritesPixelData_8Bit(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) 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] [Fact]
public void CollectColorRedTransforms_Works() => RunCollectColorRedTransformsTest(); public void CollectColorRedTransforms_Works() => RunCollectColorRedTransformsTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void CollectColorBlueTransforms_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.AllowAll); public void CollectColorBlueTransforms_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.AllowAll);
@ -86,7 +85,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact] [Fact]
public void CollectColorRedTransforms_WithoutAvx2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.DisableAVX2); 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[] 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 }; 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); float actual = LosslessUtils.CombinedShannonEntropy(x, y);
@ -147,9 +147,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
{ {
// arrange // arrange
uint[] topData = { 4278258949, 4278258949 }; uint[] topData = { 4278258949, 4278258949 };
uint left = 4294839812; const uint left = 4294839812;
short[] scratch = new short[8]; short[] scratch = new short[8];
uint expectedResult = 4294839812; const uint expectedResult = 4294839812;
// act // act
unsafe unsafe
@ -168,8 +168,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
{ {
// arrange // arrange
uint[] topData = { 4294844413, 4294779388 }; uint[] topData = { 4294844413, 4294779388 };
uint left = 4294844413; const uint left = 4294844413;
uint expectedResult = 4294779388; const uint expectedResult = 4294779388;
// act // act
unsafe unsafe
@ -188,8 +188,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
{ {
// arrange // arrange
uint[] topData = { 4278193922, 4278193666 }; uint[] topData = { 4278193922, 4278193666 };
uint left = 4278193410; const uint left = 4278193410;
uint expectedResult = 4278193154; const uint expectedResult = 4278193154;
// act // act
unsafe unsafe
@ -228,7 +228,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact] [Fact]
public void TransformColorInverse_Works() => RunTransformColorInverseTest(); public void TransformColorInverse_Works() => RunTransformColorInverseTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void CombinedShannonEntropy_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCombinedShannonEntropyTest, HwIntrinsics.AllowAll); public void CombinedShannonEntropy_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCombinedShannonEntropyTest, HwIntrinsics.AllowAll);
@ -288,6 +287,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact] [Fact]
public void TransformColorInverse_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.DisableAVX2); 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] [Fact]
public void HadamardTransform_Works() => RunHadamardTransformTest(); public void HadamardTransform_Works() => RunHadamardTransformTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void TransformTwo_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformTwoTest, HwIntrinsics.AllowAll); public void TransformTwo_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformTwoTest, HwIntrinsics.AllowAll);
@ -352,6 +351,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact] [Fact]
public void HadamardTransform_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunHadamardTransformTest, HwIntrinsics.DisableHWIntrinsic); 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;
using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Formats.Webp.Lossless;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
#if SUPPORTS_RUNTIME_INTRINSICS
using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities;
#endif
using Xunit; using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Webp namespace SixLabors.ImageSharp.Tests.Formats.Webp
@ -24,7 +22,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public static void ColorSpaceTransform_WithPeakImage_ProducesExpectedData() public static void ColorSpaceTransform_WithPeakImage_ProducesExpectedData()
=> RunColorSpaceTransformTestWithPeakImage(); => RunColorSpaceTransformTestWithPeakImage();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void ColorSpaceTransform_WithPeakImage_WithHardwareIntrinsics_Works() public void ColorSpaceTransform_WithPeakImage_WithHardwareIntrinsics_Works()
=> FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithPeakImage_ProducesExpectedData, HwIntrinsics.AllowAll); => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithPeakImage_ProducesExpectedData, HwIntrinsics.AllowAll);
@ -44,7 +41,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact] [Fact]
public void ColorSpaceTransform_WithBikeImage_WithoutAvx2_Works() public void ColorSpaceTransform_WithBikeImage_WithoutAvx2_Works()
=> FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithBikeImage_ProducesExpectedData, HwIntrinsics.DisableAVX2); => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithBikeImage_ProducesExpectedData, HwIntrinsics.DisableAVX2);
#endif
// Test image: Input\Webp\peak.png // Test image: Input\Webp\peak.png
private static void RunColorSpaceTransformTestWithPeakImage() private static void RunColorSpaceTransformTestWithPeakImage()
@ -91,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
using var image = Image.Load<Rgba32>(imgBytes); using var image = Image.Load<Rgba32>(imgBytes);
uint[] bgra = ToBgra(image); uint[] bgra = ToBgra(image);
int colorTransformBits = 3; const int colorTransformBits = 3;
int transformWidth = LosslessUtils.SubSampleSize(image.Width, colorTransformBits); int transformWidth = LosslessUtils.SubSampleSize(image.Width, colorTransformBits);
int transformHeight = LosslessUtils.SubSampleSize(image.Height, colorTransformBits); int transformHeight = LosslessUtils.SubSampleSize(image.Height, colorTransformBits);
uint[] transformData = new uint[transformWidth * transformHeight]; 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()); using var image = Image.Load<Rgba32>(imgBytes, new WebpDecoder());
uint[] bgra = ToBgra(image); uint[] bgra = ToBgra(image);
int colorTransformBits = 4; const int colorTransformBits = 4;
int transformWidth = LosslessUtils.SubSampleSize(image.Width, colorTransformBits); int transformWidth = LosslessUtils.SubSampleSize(image.Width, colorTransformBits);
int transformHeight = LosslessUtils.SubSampleSize(image.Height, colorTransformBits); int transformHeight = LosslessUtils.SubSampleSize(image.Height, colorTransformBits);
uint[] transformData = new uint[transformWidth * transformHeight]; 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] [Fact]
public void QuantizeBlock_Works() => RunQuantizeBlockTest(); public void QuantizeBlock_Works() => RunQuantizeBlockTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void QuantizeBlock_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.AllowAll); public void QuantizeBlock_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.AllowAll);
@ -51,6 +50,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact] [Fact]
public void QuantizeBlock_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.DisableAVX2); 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] [Fact]
public void TwoInverseTransform_Works() => RunTwoInverseTransformTest(); public void TwoInverseTransform_Works() => RunTwoInverseTransformTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void FTransform2_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunFTransform2Test, HwIntrinsics.AllowAll); public void FTransform2_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunFTransform2Test, HwIntrinsics.AllowAll);
@ -162,6 +161,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact] [Fact]
public void TwoInverseTransform_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTwoInverseTransformTest, HwIntrinsics.DisableHWIntrinsic); 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); Assert.Equal(1054, alpha);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void CollectHistogramTest_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectHistogramTest, HwIntrinsics.AllowAll); public void CollectHistogramTest_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectHistogramTest, HwIntrinsics.AllowAll);
[Fact] [Fact]
public void CollectHistogramTest_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectHistogramTest, HwIntrinsics.DisableHWIntrinsic); 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] [Fact]
public void AddVector_Works() => RunAddVectorTest(); public void AddVector_Works() => RunAddVectorTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void AddVector_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddVectorTest, HwIntrinsics.AllowAll); public void AddVector_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddVectorTest, HwIntrinsics.AllowAll);
[Fact] [Fact]
public void AddVector_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddVectorTest, HwIntrinsics.DisableAVX2); 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] [Fact]
public void RunSetCoeffsTest_Works() => RunSetCoeffsTest(); public void RunSetCoeffsTest_Works() => RunSetCoeffsTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void RunSetCoeffsTest_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.AllowAll); public void RunSetCoeffsTest_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.AllowAll);
[Fact] [Fact]
public void RunSetCoeffsTest_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.DisableHWIntrinsic); 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 System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using Xunit;
#if SUPPORTS_RUNTIME_INTRINSICS
using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities;
#endif using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Webp namespace SixLabors.ImageSharp.Tests.Formats.Webp
{ {
@ -21,7 +19,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact] [Fact]
public void CheckNonOpaque_WithNoneOpaquePixels_Works() => RunCheckNoneOpaqueWithNoneOpaquePixelsTest(); public void CheckNonOpaque_WithNoneOpaquePixels_Works() => RunCheckNoneOpaqueWithNoneOpaquePixelsTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void CheckNonOpaque_WithOpaquePixels_WithHardwareIntrinsics_Works() public void CheckNonOpaque_WithOpaquePixels_WithHardwareIntrinsics_Works()
=> FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithOpaquePixelsTest, HwIntrinsics.AllowAll); => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithOpaquePixelsTest, HwIntrinsics.AllowAll);
@ -45,7 +42,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact] [Fact]
public void CheckNonOpaque_WithNoneOpaquePixels_WithoutAvx2_Works() public void CheckNonOpaque_WithNoneOpaquePixels_WithoutAvx2_Works()
=> FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithNoneOpaquePixelsTest, HwIntrinsics.DisableAVX2); => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithNoneOpaquePixelsTest, HwIntrinsics.DisableAVX2);
#endif
private static void RunCheckNoneOpaqueWithNoneOpaquePixelsTest() private static void RunCheckNoneOpaqueWithNoneOpaquePixelsTest()
{ {
@ -200,7 +196,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
for (int length = 8; length < row.Length; length += 8) for (int length = 8; length < row.Length; length += 8)
{ {
// act // act
noneOpaque = WebpCommonUtils.CheckNonOpaque(row.Slice(0, length)); noneOpaque = WebpCommonUtils.CheckNonOpaque(row[..length]);
// assert // assert
Assert.False(noneOpaque); Assert.False(noneOpaque);

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

@ -44,14 +44,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
int expectedBitsPerPixel) int expectedBitsPerPixel)
{ {
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false)) using var stream = new MemoryStream(testFile.Bytes, false);
{ IImageInfo imageInfo = Image.Identify(stream);
IImageInfo imageInfo = Image.Identify(stream); Assert.NotNull(imageInfo);
Assert.NotNull(imageInfo); Assert.Equal(expectedWidth, imageInfo.Width);
Assert.Equal(expectedWidth, imageInfo.Width); Assert.Equal(expectedHeight, imageInfo.Height);
Assert.Equal(expectedHeight, imageInfo.Height); Assert.Equal(expectedBitsPerPixel, imageInfo.PixelType.BitsPerPixel);
Assert.Equal(expectedBitsPerPixel, imageInfo.PixelType.BitsPerPixel);
}
} }
[Theory] [Theory]
@ -67,11 +65,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithoutFilter<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossy_WithoutFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -83,11 +79,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithSimpleFilter<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossy_WithSimpleFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -106,11 +100,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithComplexFilter<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossy_WithComplexFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -121,11 +113,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_VerySmall<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossy_VerySmall<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -140,11 +130,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithPartitions<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossy_WithPartitions<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -154,11 +142,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithSegmentation<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossy_WithSegmentation<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -171,11 +157,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithSharpnessLevel<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossy_WithSharpnessLevel<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -196,11 +180,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossy_WithAlpha<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossy_WithAlpha<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -208,11 +190,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithAlpha<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossless_WithAlpha<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -221,11 +201,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithoutTransforms<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossless_WithoutTransforms<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -240,11 +218,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
TestImageProvider<TPixel> provider) TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -256,11 +232,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithColorIndexTransform<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossless_WithColorIndexTransform<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -269,11 +243,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithPredictorTransform<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossless_WithPredictorTransform<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -282,11 +254,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithCrossColorTransform<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossless_WithCrossColorTransform<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -306,11 +276,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithTwoTransforms<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossless_WithTwoTransforms<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -325,11 +293,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void WebpDecoder_CanDecode_Lossless_WithThreeTransforms<TPixel>(TestImageProvider<TPixel> provider) public void WebpDecoder_CanDecode_Lossless_WithThreeTransforms<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -337,18 +303,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void Decode_AnimatedLossless_VerifyAllFrames<TPixel>(TestImageProvider<TPixel> provider) public void Decode_AnimatedLossless_VerifyAllFrames<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata();
WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata(); WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
image.DebugSaveMultiFrame(provider);
image.DebugSaveMultiFrame(provider); image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact);
image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact);
Assert.Equal(0, webpMetaData.AnimationLoopCount);
Assert.Equal(0, webpMetaData.AnimationLoopCount); Assert.Equal(150U, frameMetaData.FrameDuration);
Assert.Equal(150U, frameMetaData.FrameDuration); Assert.Equal(12, image.Frames.Count);
Assert.Equal(12, image.Frames.Count);
}
} }
[Theory] [Theory]
@ -356,18 +320,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void Decode_AnimatedLossy_VerifyAllFrames<TPixel>(TestImageProvider<TPixel> provider) public void Decode_AnimatedLossy_VerifyAllFrames<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata();
WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata(); WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
image.DebugSaveMultiFrame(provider);
image.DebugSaveMultiFrame(provider); image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Tolerant(0.04f));
image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Tolerant(0.04f));
Assert.Equal(0, webpMetaData.AnimationLoopCount);
Assert.Equal(0, webpMetaData.AnimationLoopCount); Assert.Equal(150U, frameMetaData.FrameDuration);
Assert.Equal(150U, frameMetaData.FrameDuration); Assert.Equal(12, image.Frames.Count);
Assert.Equal(12, image.Frames.Count);
}
} }
[Theory] [Theory]
@ -375,10 +337,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
public void Decode_AnimatedLossless_WithFrameDecodingModeFirst_OnlyDecodesOneFrame<TPixel>(TestImageProvider<TPixel> provider) public void Decode_AnimatedLossless_WithFrameDecodingModeFirst_OnlyDecodesOneFrame<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(new WebpDecoder() { DecodingMode = FrameDecodingMode.First })) using Image<TPixel> image = provider.GetImage(new WebpDecoder() { DecodingMode = FrameDecodingMode.First });
{ Assert.Equal(1, image.Frames.Count);
Assert.Equal(1, image.Frames.Count);
}
} }
[Theory] [Theory]
@ -389,10 +349,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// Just make sure no exception is thrown. The reference decoder fails to load the image. // Just make sure no exception is thrown. The reference decoder fails to load the image.
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider);
}
} }
// https://github.com/SixLabors/ImageSharp/issues/1594 // 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) public void WebpDecoder_CanDecode_Issue1594<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using Image<TPixel> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Theory] [Theory]
@ -420,45 +376,36 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
} }
}); });
#if SUPPORTS_RUNTIME_INTRINSICS
private static void RunDecodeLossyWithHorizontalFilter() private static void RunDecodeLossyWithHorizontalFilter()
{ {
var provider = TestImageProvider<Rgba32>.File(TestImageLossyHorizontalFilterPath); var provider = TestImageProvider<Rgba32>.File(TestImageLossyHorizontalFilterPath);
using (Image<Rgba32> image = provider.GetImage(WebpDecoder)) using Image<Rgba32> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
private static void RunDecodeLossyWithVerticalFilter() private static void RunDecodeLossyWithVerticalFilter()
{ {
var provider = TestImageProvider<Rgba32>.File(TestImageLossyVerticalFilterPath); var provider = TestImageProvider<Rgba32>.File(TestImageLossyVerticalFilterPath);
using (Image<Rgba32> image = provider.GetImage(WebpDecoder)) using Image<Rgba32> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
private static void RunDecodeLossyWithSimpleFilterTest() private static void RunDecodeLossyWithSimpleFilterTest()
{ {
var provider = TestImageProvider<Rgba32>.File(TestImageLossySimpleFilterPath); var provider = TestImageProvider<Rgba32>.File(TestImageLossySimpleFilterPath);
using (Image<Rgba32> image = provider.GetImage(WebpDecoder)) using Image<Rgba32> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
private static void RunDecodeLossyWithComplexFilterTest() private static void RunDecodeLossyWithComplexFilterTest()
{ {
var provider = TestImageProvider<Rgba32>.File(TestImageLossyComplexFilterPath); var provider = TestImageProvider<Rgba32>.File(TestImageLossyComplexFilterPath);
using (Image<Rgba32> image = provider.GetImage(WebpDecoder)) using Image<Rgba32> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Fact] [Fact]
@ -472,6 +419,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[Fact] [Fact]
public void DecodeLossyWithComplexFilterTest_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunDecodeLossyWithComplexFilterTest, HwIntrinsics.DisableHWIntrinsic); 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)); image.VerifyEncoder(provider, "webp", string.Empty, encoder, ImageComparer.Tolerant(0.04f));
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void RunEncodeLossy_WithPeakImage_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunEncodeLossy_WithPeakImage, HwIntrinsics.AllowAll); public void RunEncodeLossy_WithPeakImage_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunEncodeLossy_WithPeakImage, HwIntrinsics.AllowAll);
[Fact] [Fact]
public void RunEncodeLossy_WithPeakImage_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunEncodeLossy_WithPeakImage, HwIntrinsics.DisableHWIntrinsic); public void RunEncodeLossy_WithPeakImage_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunEncodeLossy_WithPeakImage, HwIntrinsics.DisableHWIntrinsic);
#endif
private static ImageComparer GetComparer(int quality) 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() public static void RunUpSampleYuvToRgbTest()
{ {
var provider = TestImageProvider<Rgba32>.File(TestImageLossyFullPath); var provider = TestImageProvider<Rgba32>.File(TestImageLossyFullPath);
using (Image<Rgba32> image = provider.GetImage(WebpDecoder)) using Image<Rgba32> image = provider.GetImage(WebpDecoder);
{ image.DebugSave(provider);
image.DebugSave(provider); image.CompareToOriginal(provider, ReferenceDecoder);
image.CompareToOriginal(provider, ReferenceDecoder);
}
} }
[Fact] [Fact]
public void UpSampleYuvToRgb_Works() => RunUpSampleYuvToRgbTest(); public void UpSampleYuvToRgb_Works() => RunUpSampleYuvToRgbTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void UpSampleYuvToRgb_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.AllowAll); public void UpSampleYuvToRgb_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.AllowAll);
[Fact] [Fact]
public void UpSampleYuvToRgb_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.DisableSSE2); public void UpSampleYuvToRgb_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.DisableSSE2);
#endif
[Theory] [Theory]
[WithFile(TestImages.Webp.Yuv, PixelTypes.Rgba32)] [WithFile(TestImages.Webp.Yuv, PixelTypes.Rgba32)]
public void ConvertRgbToYuv_Works<TPixel>(TestImageProvider<TPixel> provider) public void ConvertRgbToYuv_Works<TPixel>(TestImageProvider<TPixel> provider)
@ -157,8 +152,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
// assert // assert
Assert.True(expectedY.AsSpan().SequenceEqual(y)); Assert.True(expectedY.AsSpan().SequenceEqual(y));
Assert.True(expectedU.AsSpan().SequenceEqual(u.Slice(0, expectedU.Length))); Assert.True(expectedU.AsSpan().SequenceEqual(u[..expectedU.Length]));
Assert.True(expectedV.AsSpan().SequenceEqual(v.Slice(0, expectedV.Length))); Assert.True(expectedV.AsSpan().SequenceEqual(v[..expectedV.Length]));
} }
[Theory] [Theory]
@ -263,8 +258,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
// assert // assert
Assert.True(expectedY.AsSpan().SequenceEqual(y)); Assert.True(expectedY.AsSpan().SequenceEqual(y));
Assert.True(expectedU.AsSpan().SequenceEqual(u.Slice(0, expectedU.Length))); Assert.True(expectedU.AsSpan().SequenceEqual(u[..expectedU.Length]));
Assert.True(expectedV.AsSpan().SequenceEqual(v.Slice(0, expectedV.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); Assert.Equal(mode, resizeOptions.Mode);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void HwIntrinsics_Resize() public void HwIntrinsics_Resize()
{ {
@ -106,6 +105,5 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
RunTest, RunTest,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableFMA); 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.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
#endif
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
@ -55,7 +53,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
HwIntrinsics.AllowAll); HwIntrinsics.AllowAll);
} }
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact] [Fact]
public void CanLimitHwIntrinsicBaseFeatures() public void CanLimitHwIntrinsicBaseFeatures()
{ {
@ -82,7 +79,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
AssertDisabled, AssertDisabled,
HwIntrinsics.DisableHWIntrinsic); HwIntrinsics.DisableHWIntrinsic);
} }
#endif
[Fact] [Fact]
public void CanLimitHwIntrinsicFeaturesWithIntrinsicsParam() public void CanLimitHwIntrinsicFeaturesWithIntrinsicsParam()
@ -91,9 +87,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
{ {
Assert.NotNull(intrinsic); Assert.NotNull(intrinsic);
switch ((HwIntrinsics)Enum.Parse(typeof(HwIntrinsics), intrinsic)) switch (Enum.Parse<HwIntrinsics>(intrinsic))
{ {
#if SUPPORTS_RUNTIME_INTRINSICS
case HwIntrinsics.DisableHWIntrinsic: case HwIntrinsics.DisableHWIntrinsic:
Assert.False(Sse.IsSupported); Assert.False(Sse.IsSupported);
Assert.False(Sse2.IsSupported); Assert.False(Sse2.IsSupported);
@ -156,11 +151,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
case HwIntrinsics.DisableLZCNT: case HwIntrinsics.DisableLZCNT:
Assert.False(Lzcnt.IsSupported); Assert.False(Lzcnt.IsSupported);
break; break;
#endif
} }
} }
foreach (HwIntrinsics intrinsic in (HwIntrinsics[])Enum.GetValues(typeof(HwIntrinsics))) foreach (HwIntrinsics intrinsic in Enum.GetValues<HwIntrinsics>())
{ {
FeatureTestRunner.RunWithHwIntrinsicsFeature(AssertHwIntrinsicsFeatureDisabled, intrinsic); FeatureTestRunner.RunWithHwIntrinsicsFeature(AssertHwIntrinsicsFeatureDisabled, intrinsic);
} }
@ -173,10 +167,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
{ {
Assert.NotNull(serializable); Assert.NotNull(serializable);
Assert.NotNull(FeatureTestRunner.DeserializeForXunit<FakeSerializable>(serializable)); Assert.NotNull(FeatureTestRunner.DeserializeForXunit<FakeSerializable>(serializable));
#if SUPPORTS_RUNTIME_INTRINSICS
Assert.False(Sse.IsSupported); Assert.False(Sse.IsSupported);
#endif
} }
FeatureTestRunner.RunWithHwIntrinsicsFeature( FeatureTestRunner.RunWithHwIntrinsicsFeature(
@ -193,9 +184,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
Assert.NotNull(serializable); Assert.NotNull(serializable);
Assert.NotNull(FeatureTestRunner.DeserializeForXunit<FakeSerializable>(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: case HwIntrinsics.DisableHWIntrinsic:
Assert.False(Sse.IsSupported); Assert.False(Sse.IsSupported);
Assert.False(Sse2.IsSupported); Assert.False(Sse2.IsSupported);
@ -258,7 +248,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
case HwIntrinsics.DisableLZCNT: case HwIntrinsics.DisableLZCNT:
Assert.False(Lzcnt.IsSupported); Assert.False(Lzcnt.IsSupported);
break; break;
#endif
} }
} }

Loading…
Cancel
Save