Browse Source

Merge pull request #3177 from SixLabors/js/net10-net11-targets

Retarget builds to .NET 10 and 11
pull/3178/head
James Jackson-South 2 weeks ago
committed by GitHub
parent
commit
d0930b9e4c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      .gitattributes
  2. 37
      .github/workflows/build-and-test.yml
  3. 4
      .github/workflows/code-coverage.yml
  4. 8
      Directory.Build.props
  5. 18
      global.json
  6. 2
      shared-infrastructure
  7. 4
      src/ImageSharp/Common/Extensions/StreamExtensions.cs
  8. 42
      src/ImageSharp/Common/Helpers/Numerics.cs
  9. 6
      src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs
  10. 4
      src/ImageSharp/Common/Helpers/SimdUtils.cs
  11. 6
      src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs
  12. 4
      src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
  13. 16
      src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
  14. 8
      src/ImageSharp/Formats/Exr/ExrEncoderCore.cs
  15. 4
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
  16. 4
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  17. 6
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  18. 6
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  19. 6
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  20. 6
      src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
  21. 4
      src/ImageSharp/Formats/Tiff/Compression/Compressors/T6BitCompressor.cs
  22. 6
      src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs
  23. 18
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  24. 4
      src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs
  25. 12
      src/ImageSharp/Formats/Webp/AlphaDecoder.cs
  26. 4
      src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
  27. 4
      src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
  28. 4
      src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs
  29. 6
      src/ImageSharp/Formats/Webp/Lossless/ColorSpaceTransformUtils.cs
  30. 2
      src/ImageSharp/Formats/Webp/Lossless/CostModel.cs
  31. 2
      src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs
  32. 8
      src/ImageSharp/Formats/Webp/Lossless/HuffmanUtils.cs
  33. 4
      src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs
  34. 2
      src/ImageSharp/Formats/Webp/Lossless/NearLosslessEnc.cs
  35. 10
      src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs
  36. 8
      src/ImageSharp/Formats/Webp/Lossless/Vp8LBitEntropy.cs
  37. 16
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  38. 10
      src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs
  39. 4
      src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs
  40. 50
      src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs
  41. 2
      src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs
  42. 2
      src/ImageSharp/Formats/Webp/Lossy/Vp8EncProba.cs
  43. 28
      src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs
  44. 2
      src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs
  45. 2
      src/ImageSharp/Formats/Webp/Lossy/Vp8SegmentInfo.cs
  46. 10
      src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs
  47. 4
      src/ImageSharp/ImageSharp.csproj
  48. 2
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
  49. 2
      tests/Directory.Build.targets
  50. 4
      tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
  51. 4
      tests/ImageSharp.PublicApi.Tests/ImageSharp.PublicApi.Tests.csproj
  52. 4
      tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj
  53. 10
      tests/ImageSharp.Tests/IO/LocalFileSystemTests.cs
  54. 4
      tests/ImageSharp.Tests/ImageSharp.Tests.csproj
  55. 7
      tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs
  56. 28
      tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs

1
.gitattributes

@ -110,6 +110,7 @@
############################################################################### ###############################################################################
# Handle image files by git lfs # Handle image files by git lfs
############################################################################### ###############################################################################
*.pdf filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text *.jpeg filter=lfs diff=lfs merge=lfs -text
*.bmp filter=lfs diff=lfs merge=lfs -text *.bmp filter=lfs diff=lfs merge=lfs -text

37
.github/workflows/build-and-test.yml

@ -64,48 +64,48 @@ jobs:
matrix: matrix:
options: options:
- os: ubuntu-latest - os: ubuntu-latest
framework: net10.0 framework: net11.0
sdk: 10.0.x sdk: 11.0.x
sdk-preview: true sdk-preview: true
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: macos-26 - os: macos-26
framework: net10.0 framework: net11.0
sdk: 10.0.x sdk: 11.0.x
sdk-preview: true sdk-preview: true
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: windows-latest - os: windows-latest
framework: net10.0 framework: net11.0
sdk: 10.0.x sdk: 11.0.x
sdk-preview: true sdk-preview: true
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: ubuntu-22.04-arm - os: ubuntu-22.04-arm
framework: net10.0 framework: net11.0
sdk: 10.0.x sdk: 11.0.x
sdk-preview: true sdk-preview: true
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: ubuntu-latest - os: ubuntu-latest
framework: net8.0 framework: net10.0
sdk: 8.0.x sdk: 10.0.x
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: macos-26 - os: macos-26
framework: net8.0 framework: net10.0
sdk: 8.0.x sdk: 10.0.x
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: windows-latest - os: windows-latest
framework: net8.0 framework: net10.0
sdk: 8.0.x sdk: 10.0.x
runtime: -x64 runtime: -x64
codecov: false codecov: false
- os: ubuntu-22.04-arm - os: ubuntu-22.04-arm
framework: net8.0 framework: net10.0
sdk: 8.0.x sdk: 10.0.x
runtime: -x64 runtime: -x64
codecov: false codecov: false
@ -169,14 +169,15 @@ jobs:
uses: actions/setup-dotnet@v6 uses: actions/setup-dotnet@v6
with: with:
dotnet-version: | dotnet-version: |
8.0.x 10.0.x
- name: DotNet Setup Preview - name: DotNet Setup Preview
if: ${{ matrix.options.sdk-preview == true }} if: ${{ matrix.options.sdk-preview == true }}
uses: actions/setup-dotnet@v6 uses: actions/setup-dotnet@v6
with: with:
dotnet-quality: preview
dotnet-version: | dotnet-version: |
10.0.x 11.0.x
- name: DotNet Build - name: DotNet Build
if: ${{ matrix.options.sdk-preview != true }} if: ${{ matrix.options.sdk-preview != true }}

4
.github/workflows/code-coverage.yml

@ -11,7 +11,7 @@ jobs:
matrix: matrix:
options: options:
- os: ubuntu-latest - os: ubuntu-latest
framework: net8.0 framework: net10.0
runtime: -x64 runtime: -x64
codecov: true codecov: true
@ -70,7 +70,7 @@ jobs:
uses: actions/setup-dotnet@v6 uses: actions/setup-dotnet@v6
with: with:
dotnet-version: | dotnet-version: |
8.0.x 10.0.x
- name: DotNet Build - name: DotNet Build
shell: pwsh shell: pwsh

8
Directory.Build.props

@ -21,14 +21,6 @@
<!-- Import the shared global .props file --> <!-- Import the shared global .props file -->
<Import Project="$(MSBuildThisFileDirectory)shared-infrastructure\msbuild\props\SixLabors.Global.props" /> <Import Project="$(MSBuildThisFileDirectory)shared-infrastructure\msbuild\props\SixLabors.Global.props" />
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0' OR '$(TargetFramework)' == 'net9.0'">
<LangVersion>12.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net10.0'">
<LangVersion>14.0</LangVersion>
</PropertyGroup>
<!-- <!--
Ensure all custom build configurations based upon "Release" are optimized. Ensure all custom build configurations based upon "Release" are optimized.
This is easier than setting each project individually. This is easier than setting each project individually.

18
global.json

@ -0,0 +1,18 @@
// Pins the .NET SDK to major version 10 or later for all SixLabors repositories.
// The build copies this file to consuming repositories together with .editorconfig and
// the other shared config files (see msbuild/targets/SixLabors.Src.targets).
{
"sdk": {
// The floor, not an exact build: resolution never selects an SDK below major 10.
"version": "10.0.0",
// Select the newest installed SDK at or above the floor. This spans feature bands,
// minor versions, and major versions, so a machine with only stable 10.0.x SDKs
// resolves the newest of those, and a machine with a newer SDK resolves that instead.
"rollForward": "latestMajor",
// Let the CLI and Visual Studio select preview SDKs when one is installed.
// Machines and CI lanes without a preview SDK are unaffected.
"allowPrerelease": true
}
}

2
shared-infrastructure

@ -1 +1 @@
Subproject commit 6949b1094c3ccf672b93608f3f74a8b483426d4b Subproject commit 52fc1c3e87a755b2b17ce77e8cd22ec4bea17bcb

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
@ -17,7 +17,7 @@ internal static class StreamExtensions
/// <param name="buffer">The buffer.</param> /// <param name="buffer">The buffer.</param>
/// <param name="offset">The offset within the buffer to begin writing.</param> /// <param name="offset">The offset within the buffer to begin writing.</param>
/// <param name="count">The number of bytes to write to the stream.</param> /// <param name="count">The number of bytes to write to the stream.</param>
public static void Write(this Stream stream, Span<byte> buffer, int offset, int count) public static void Write(this Stream stream, ReadOnlySpan<byte> buffer, int offset, int count)
=> stream.Write(buffer.Slice(offset, count)); => stream.Write(buffer.Slice(offset, count));
/// <summary> /// <summary>

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics; using System.Numerics;
@ -939,20 +939,10 @@ internal static class Numerics
/// <typeparam name="TVector">The type of the vector.</typeparam> /// <typeparam name="TVector">The type of the vector.</typeparam>
/// <param name="span">The given span.</param> /// <param name="span">The given span.</param>
/// <returns>Count of vectors that safely fit into the span.</returns> /// <returns>Count of vectors that safely fit into the span.</returns>
public static nuint VectorCount<TVector>(this Span<byte> span) public static nuint VectorCount<TVector>(this ReadOnlySpan<byte> span)
where TVector : struct where TVector : struct
=> (uint)span.Length / (uint)Vector<TVector>.Count; => (uint)span.Length / (uint)Vector<TVector>.Count;
/// <summary>
/// Gets the count of vectors that safely fit into the given span.
/// </summary>
/// <typeparam name="TVector">The type of the vector.</typeparam>
/// <param name="span">The given span.</param>
/// <returns>Count of vectors that safely fit into the span.</returns>
public static nuint Vector128Count<TVector>(this Span<byte> span)
where TVector : struct
=> (uint)span.Length / (uint)Vector128<TVector>.Count;
/// <summary> /// <summary>
/// Gets the count of vectors that safely fit into the given span. /// Gets the count of vectors that safely fit into the given span.
/// </summary> /// </summary>
@ -963,16 +953,6 @@ internal static class Numerics
where TVector : struct where TVector : struct
=> (uint)span.Length / (uint)Vector128<TVector>.Count; => (uint)span.Length / (uint)Vector128<TVector>.Count;
/// <summary>
/// Gets the count of vectors that safely fit into the given span.
/// </summary>
/// <typeparam name="TVector">The type of the vector.</typeparam>
/// <param name="span">The given span.</param>
/// <returns>Count of vectors that safely fit into the span.</returns>
public static nuint Vector256Count<TVector>(this Span<byte> span)
where TVector : struct
=> (uint)span.Length / (uint)Vector256<TVector>.Count;
/// <summary> /// <summary>
/// Gets the count of vectors that safely fit into the given span. /// Gets the count of vectors that safely fit into the given span.
/// </summary> /// </summary>
@ -983,16 +963,6 @@ internal static class Numerics
where TVector : struct where TVector : struct
=> (uint)span.Length / (uint)Vector256<TVector>.Count; => (uint)span.Length / (uint)Vector256<TVector>.Count;
/// <summary>
/// Gets the count of vectors that safely fit into the given span.
/// </summary>
/// <typeparam name="TVector">The type of the vector.</typeparam>
/// <param name="span">The given span.</param>
/// <returns>Count of vectors that safely fit into the span.</returns>
public static nuint Vector512Count<TVector>(this Span<byte> span)
where TVector : struct
=> (uint)span.Length / (uint)Vector512<TVector>.Count;
/// <summary> /// <summary>
/// Gets the count of vectors that safely fit into the given span. /// Gets the count of vectors that safely fit into the given span.
/// </summary> /// </summary>
@ -1009,7 +979,7 @@ internal static class Numerics
/// <typeparam name="TVector">The type of the vector.</typeparam> /// <typeparam name="TVector">The type of the vector.</typeparam>
/// <param name="span">The given span.</param> /// <param name="span">The given span.</param>
/// <returns>Count of vectors that safely fit into the span.</returns> /// <returns>Count of vectors that safely fit into the span.</returns>
public static nuint VectorCount<TVector>(this Span<float> span) public static nuint VectorCount<TVector>(this ReadOnlySpan<float> span)
where TVector : struct where TVector : struct
=> (uint)span.Length / (uint)Vector<TVector>.Count; => (uint)span.Length / (uint)Vector<TVector>.Count;
@ -1019,7 +989,7 @@ internal static class Numerics
/// <typeparam name="TVector">The type of the vector.</typeparam> /// <typeparam name="TVector">The type of the vector.</typeparam>
/// <param name="span">The given span.</param> /// <param name="span">The given span.</param>
/// <returns>Count of vectors that safely fit into the span.</returns> /// <returns>Count of vectors that safely fit into the span.</returns>
public static nuint Vector128Count<TVector>(this Span<float> span) public static nuint Vector128Count<TVector>(this ReadOnlySpan<float> span)
where TVector : struct where TVector : struct
=> (uint)span.Length / (uint)Vector128<TVector>.Count; => (uint)span.Length / (uint)Vector128<TVector>.Count;
@ -1029,7 +999,7 @@ internal static class Numerics
/// <typeparam name="TVector">The type of the vector.</typeparam> /// <typeparam name="TVector">The type of the vector.</typeparam>
/// <param name="span">The given span.</param> /// <param name="span">The given span.</param>
/// <returns>Count of vectors that safely fit into the span.</returns> /// <returns>Count of vectors that safely fit into the span.</returns>
public static nuint Vector256Count<TVector>(this Span<float> span) public static nuint Vector256Count<TVector>(this ReadOnlySpan<float> span)
where TVector : struct where TVector : struct
=> (uint)span.Length / (uint)Vector256<TVector>.Count; => (uint)span.Length / (uint)Vector256<TVector>.Count;
@ -1049,7 +1019,7 @@ internal static class Numerics
/// <typeparam name="TVector">The type of the vector.</typeparam> /// <typeparam name="TVector">The type of the vector.</typeparam>
/// <param name="span">The given span.</param> /// <param name="span">The given span.</param>
/// <returns>Count of vectors that safely fit into the span.</returns> /// <returns>Count of vectors that safely fit into the span.</returns>
public static nuint Vector512Count<TVector>(this Span<float> span) public static nuint Vector512Count<TVector>(this ReadOnlySpan<float> span)
where TVector : struct where TVector : struct
=> (uint)span.Length / (uint)Vector512<TVector>.Count; => (uint)span.Length / (uint)Vector512<TVector>.Count;

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Diagnostics; using System.Diagnostics;
@ -464,7 +464,7 @@ internal static partial class SimdUtils
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]
internal static void VerifyShuffle4SpanInput<T>(ReadOnlySpan<T> source, Span<T> destination) internal static void VerifyShuffle4SpanInput<T>(ReadOnlySpan<T> source, ReadOnlySpan<T> destination)
where T : struct where T : struct
{ {
DebugGuard.IsTrue( DebugGuard.IsTrue(
@ -479,7 +479,7 @@ internal static partial class SimdUtils
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]
private static void VerifyShuffle3SpanInput<T>(ReadOnlySpan<T> source, Span<T> destination) private static void VerifyShuffle3SpanInput<T>(ReadOnlySpan<T> source, ReadOnlySpan<T> destination)
where T : struct where T : struct
{ {
DebugGuard.IsTrue( DebugGuard.IsTrue(

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

@ -72,7 +72,7 @@ internal static partial class SimdUtils
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]
private static void DebugVerifySpanInput(ReadOnlySpan<byte> source, Span<float> dest, int shouldBeDivisibleBy) private static void DebugVerifySpanInput(ReadOnlySpan<byte> source, ReadOnlySpan<float> dest, int shouldBeDivisibleBy)
{ {
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!");
DebugGuard.IsTrue( DebugGuard.IsTrue(
@ -82,7 +82,7 @@ internal static partial class SimdUtils
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]
private static void DebugVerifySpanInput(ReadOnlySpan<float> source, Span<byte> destination, int shouldBeDivisibleBy) private static void DebugVerifySpanInput(ReadOnlySpan<float> source, ReadOnlySpan<byte> destination, int shouldBeDivisibleBy)
{ {
DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!"); DebugGuard.IsTrue(source.Length == destination.Length, nameof(source), "Input spans must be of same length!");
DebugGuard.IsTrue( DebugGuard.IsTrue(

6
src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
@ -141,7 +141,7 @@ internal class B44ExrCompression : ExrBaseDecompressor
/// </summary> /// </summary>
/// <param name="b">The source byte data to unpack.</param> /// <param name="b">The source byte data to unpack.</param>
/// <param name="s">Destintation buffer.</param> /// <param name="s">Destintation buffer.</param>
private static void Unpack14(Span<byte> b, Span<ushort> s) private static void Unpack14(ReadOnlySpan<byte> b, Span<ushort> s)
{ {
s[0] = (ushort)((b[0] << 8) | b[1]); s[0] = (ushort)((b[0] << 8) | b[1]);
@ -185,7 +185,7 @@ internal class B44ExrCompression : ExrBaseDecompressor
/// </summary> /// </summary>
/// <param name="b">The source byte data to unpack.</param> /// <param name="b">The source byte data to unpack.</param>
/// <param name="s">The destination buffer.</param> /// <param name="s">The destination buffer.</param>
private static void Unpack3(Span<byte> b, Span<ushort> s) private static void Unpack3(ReadOnlySpan<byte> b, Span<ushort> s)
{ {
s[0] = (ushort)((b[0] << 8) | b[1]); s[0] = (ushort)((b[0] << 8) | b[1]);

4
src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.IO.Compression; using System.IO.Compression;
@ -98,7 +98,7 @@ internal abstract class ExrBaseDecompressor : ExrBaseCompression
/// <param name="source">The source data.</param> /// <param name="source">The source data.</param>
/// <param name="unCompressedBytes">The uncompressed bytes.</param> /// <param name="unCompressedBytes">The uncompressed bytes.</param>
/// <param name="output">The output to write to.</param> /// <param name="output">The output to write to.</param>
protected static void Interleave(Span<byte> source, uint unCompressedBytes, Span<byte> output) protected static void Interleave(ReadOnlySpan<byte> source, uint unCompressedBytes, Span<byte> output)
{ {
int sourceOffset = 0; int sourceOffset = 0;
int offset0 = 0; int offset0 = 0;

16
src/ImageSharp/Formats/Exr/ExrDecoderCore.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable #nullable disable
@ -320,7 +320,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
private static int ReadFloatChannelData( private static int ReadFloatChannelData(
BufferedReadStream stream, BufferedReadStream stream,
ExrChannelInfo channel, ExrChannelInfo channel,
Span<byte> decompressedPixelData, ReadOnlySpan<byte> decompressedPixelData,
Span<float> redPixelData, Span<float> redPixelData,
Span<float> greenPixelData, Span<float> greenPixelData,
Span<float> bluePixelData, Span<float> bluePixelData,
@ -371,7 +371,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
private int ReadUnsignedIntChannelData( private int ReadUnsignedIntChannelData(
BufferedReadStream stream, BufferedReadStream stream,
ExrChannelInfo channel, ExrChannelInfo channel,
Span<byte> decompressedPixelData, ReadOnlySpan<byte> decompressedPixelData,
Span<uint> redPixelData, Span<uint> redPixelData,
Span<uint> greenPixelData, Span<uint> greenPixelData,
Span<uint> bluePixelData, Span<uint> bluePixelData,
@ -414,7 +414,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
/// <param name="pixelData">The pixel data as float.</param> /// <param name="pixelData">The pixel data as float.</param>
/// <param name="width">The width in pixel of a row.</param> /// <param name="width">The width in pixel of a row.</param>
/// <returns>The bytes read.</returns> /// <returns>The bytes read.</returns>
private static int ReadChannelData(ExrChannelInfo channel, Span<byte> decompressedPixelData, Span<float> pixelData, int width) => channel.PixelType switch private static int ReadChannelData(ExrChannelInfo channel, ReadOnlySpan<byte> decompressedPixelData, Span<float> pixelData, int width) => channel.PixelType switch
{ {
ExrPixelType.Half => ReadPixelRowChannelHalfSingle(decompressedPixelData, pixelData, width), ExrPixelType.Half => ReadPixelRowChannelHalfSingle(decompressedPixelData, pixelData, width),
ExrPixelType.Float => ReadPixelRowChannelSingle(decompressedPixelData, pixelData, width), ExrPixelType.Float => ReadPixelRowChannelSingle(decompressedPixelData, pixelData, width),
@ -429,7 +429,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
/// <param name="pixelData">The pixel data as uint.</param> /// <param name="pixelData">The pixel data as uint.</param>
/// <param name="width">The width in pixels.</param> /// <param name="width">The width in pixels.</param>
/// <returns>The bytes read.</returns> /// <returns>The bytes read.</returns>
private static int ReadChannelData(ExrChannelInfo channel, Span<byte> decompressedPixelData, Span<uint> pixelData, int width) => channel.PixelType switch private static int ReadChannelData(ExrChannelInfo channel, ReadOnlySpan<byte> decompressedPixelData, Span<uint> pixelData, int width) => channel.PixelType switch
{ {
ExrPixelType.UnsignedInt => ReadPixelRowChannelUnsignedInt(decompressedPixelData, pixelData, width), ExrPixelType.UnsignedInt => ReadPixelRowChannelUnsignedInt(decompressedPixelData, pixelData, width),
_ => 0, _ => 0,
@ -442,7 +442,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
/// <param name="channelData">The channel data as float.</param> /// <param name="channelData">The channel data as float.</param>
/// <param name="width">The width of a row in pixels.</param> /// <param name="width">The width of a row in pixels.</param>
/// <returns>The bytes read.</returns> /// <returns>The bytes read.</returns>
private static int ReadPixelRowChannelHalfSingle(Span<byte> decompressedPixelData, Span<float> channelData, int width) private static int ReadPixelRowChannelHalfSingle(ReadOnlySpan<byte> decompressedPixelData, Span<float> channelData, int width)
{ {
int offset = 0; int offset = 0;
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
@ -462,7 +462,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
/// <param name="channelData">The pixel data as float.</param> /// <param name="channelData">The pixel data as float.</param>
/// <param name="width">The width in pixels of a row.</param> /// <param name="width">The width in pixels of a row.</param>
/// <returns>The bytes read.</returns> /// <returns>The bytes read.</returns>
private static int ReadPixelRowChannelSingle(Span<byte> decompressedPixelData, Span<float> channelData, int width) private static int ReadPixelRowChannelSingle(ReadOnlySpan<byte> decompressedPixelData, Span<float> channelData, int width)
{ {
int offset = 0; int offset = 0;
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
@ -482,7 +482,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
/// <param name="channelData">The uint pixel data.</param> /// <param name="channelData">The uint pixel data.</param>
/// <param name="width">The width of a row in pixels.</param> /// <param name="width">The width of a row in pixels.</param>
/// <returns>The bytes read.</returns> /// <returns>The bytes read.</returns>
private static int ReadPixelRowChannelUnsignedInt(Span<byte> decompressedPixelData, Span<uint> channelData, int width) private static int ReadPixelRowChannelUnsignedInt(ReadOnlySpan<byte> decompressedPixelData, Span<uint> channelData, int width)
{ {
int offset = 0; int offset = 0;
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)

8
src/ImageSharp/Formats/Exr/ExrEncoderCore.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
@ -364,7 +364,7 @@ internal sealed class ExrEncoderCore
/// <param name="blueBuffer">The blue channel buffer.</param> /// <param name="blueBuffer">The blue channel buffer.</param>
/// <param name="greenBuffer">The green channel buffer.</param> /// <param name="greenBuffer">The green channel buffer.</param>
/// <param name="redBuffer">The red channel buffer.</param> /// <param name="redBuffer">The red channel buffer.</param>
private static void WriteSingleRow(Span<byte> buffer, int width, Span<float> alphaBuffer, Span<float> blueBuffer, Span<float> greenBuffer, Span<float> redBuffer) private static void WriteSingleRow(Span<byte> buffer, int width, ReadOnlySpan<float> alphaBuffer, ReadOnlySpan<float> blueBuffer, ReadOnlySpan<float> greenBuffer, ReadOnlySpan<float> redBuffer)
{ {
int offset = 0; int offset = 0;
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
@ -401,7 +401,7 @@ internal sealed class ExrEncoderCore
/// <param name="blueBuffer">The blue channel buffer.</param> /// <param name="blueBuffer">The blue channel buffer.</param>
/// <param name="greenBuffer">The green channel buffer.</param> /// <param name="greenBuffer">The green channel buffer.</param>
/// <param name="redBuffer">The red channel buffer.</param> /// <param name="redBuffer">The red channel buffer.</param>
private static void WriteHalfSingleRow(Span<byte> buffer, int width, Span<float> alphaBuffer, Span<float> blueBuffer, Span<float> greenBuffer, Span<float> redBuffer) private static void WriteHalfSingleRow(Span<byte> buffer, int width, ReadOnlySpan<float> alphaBuffer, ReadOnlySpan<float> blueBuffer, ReadOnlySpan<float> greenBuffer, ReadOnlySpan<float> redBuffer)
{ {
int offset = 0; int offset = 0;
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
@ -438,7 +438,7 @@ internal sealed class ExrEncoderCore
/// <param name="blueBuffer">The blue channel buffer.</param> /// <param name="blueBuffer">The blue channel buffer.</param>
/// <param name="greenBuffer">The green channel buffer.</param> /// <param name="greenBuffer">The green channel buffer.</param>
/// <param name="redBuffer">The red channel buffer.</param> /// <param name="redBuffer">The red channel buffer.</param>
private static void WriteUnsignedIntRow(Span<byte> buffer, int width, Span<uint> alphaBuffer, Span<uint> blueBuffer, Span<uint> greenBuffer, Span<uint> redBuffer) private static void WriteUnsignedIntRow(Span<byte> buffer, int width, ReadOnlySpan<uint> alphaBuffer, ReadOnlySpan<uint> blueBuffer, ReadOnlySpan<uint> greenBuffer, ReadOnlySpan<uint> redBuffer)
{ {
int offset = 0; int offset = 0;
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics; using System.Numerics;
@ -129,7 +129,7 @@ internal partial struct Block8x8
/// <summary> /// <summary>
/// Cast and copy <see cref="Size"/> <see cref="int"/>-s from the beginning of 'source' span. /// Cast and copy <see cref="Size"/> <see cref="int"/>-s from the beginning of 'source' span.
/// </summary> /// </summary>
public void LoadFrom(Span<int> source) public void LoadFrom(ReadOnlySpan<int> source)
{ {
for (int i = 0; i < Size; i++) for (int i = 0; i < Size; i++)
{ {

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics; using System.Numerics;
@ -115,7 +115,7 @@ internal partial struct Block8x8F : IEquatable<Block8x8F>
/// Load raw 32bit floating point data from source /// Load raw 32bit floating point data from source
/// </summary> /// </summary>
/// <param name="source">Source</param> /// <param name="source">Source</param>
public unsafe void LoadFrom(Span<int> source) public unsafe void LoadFrom(ReadOnlySpan<int> source)
{ {
fixed (Vector4* ptr = &this.V0L) fixed (Vector4* ptr = &this.V0L)
{ {

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable #nullable disable
@ -1126,7 +1126,7 @@ internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData
/// <param name="blockDataSpan">The span holding the block resource data.</param> /// <param name="blockDataSpan">The span holding the block resource data.</param>
/// <returns>The length of the name.</returns> /// <returns>The length of the name.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int ReadImageResourceNameLength(Span<byte> blockDataSpan) private static int ReadImageResourceNameLength(ReadOnlySpan<byte> blockDataSpan)
{ {
byte nameLength = blockDataSpan[2]; byte nameLength = blockDataSpan[2];
int nameDataSize = nameLength == 0 ? 2 : nameLength; int nameDataSize = nameLength == 0 ? 2 : nameLength;
@ -1145,7 +1145,7 @@ internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData
/// <param name="resourceBlockNameLength">The length of the block name.</param> /// <param name="resourceBlockNameLength">The length of the block name.</param>
/// <returns>The block length.</returns> /// <returns>The block length.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int ReadResourceDataLength(Span<byte> blockDataSpan, int resourceBlockNameLength) private static int ReadResourceDataLength(ReadOnlySpan<byte> blockDataSpan, int resourceBlockNameLength)
=> BinaryPrimitives.ReadInt32BigEndian(blockDataSpan.Slice(2 + resourceBlockNameLength, 4)); => BinaryPrimitives.ReadInt32BigEndian(blockDataSpan.Slice(2 + resourceBlockNameLength, 4));
/// <summary> /// <summary>

6
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable #nullable disable
@ -620,7 +620,7 @@ internal sealed unsafe partial class JpegEncoderCore
/// </summary> /// </summary>
/// <param name="components">The collecction of component configuration items.</param> /// <param name="components">The collecction of component configuration items.</param>
/// <param name="buffer">Temporary buffer.</param> /// <param name="buffer">Temporary buffer.</param>
private void WriteStartOfScan(Span<JpegComponentConfig> components, Span<byte> buffer) => private void WriteStartOfScan(ReadOnlySpan<JpegComponentConfig> components, Span<byte> buffer) =>
this.WriteStartOfScan(components, buffer, 0x00, 0x3f); this.WriteStartOfScan(components, buffer, 0x00, 0x3f);
/// <summary> /// <summary>
@ -630,7 +630,7 @@ internal sealed unsafe partial class JpegEncoderCore
/// <param name="buffer">Temporary buffer.</param> /// <param name="buffer">Temporary buffer.</param>
/// <param name="spectralStart">Start of spectral selection</param> /// <param name="spectralStart">Start of spectral selection</param>
/// <param name="spectralEnd">End of spectral selection</param> /// <param name="spectralEnd">End of spectral selection</param>
private void WriteStartOfScan(Span<JpegComponentConfig> components, Span<byte> buffer, byte spectralStart, byte spectralEnd) private void WriteStartOfScan(ReadOnlySpan<JpegComponentConfig> components, Span<byte> buffer, byte spectralStart, byte spectralEnd)
{ {
// Write the SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes: // Write the SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes:
// - the marker length "\x00\x0c", // - the marker length "\x00\x0c",

6
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -1541,7 +1541,7 @@ internal sealed class PngEncoderCore : IDisposable
/// <param name="stream">The <see cref="Stream"/> to write to.</param> /// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="type">The type of chunk to write.</param> /// <param name="type">The type of chunk to write.</param>
/// <param name="data">The <see cref="T:byte[]"/> containing data.</param> /// <param name="data">The <see cref="T:byte[]"/> containing data.</param>
private void WriteChunk(Stream stream, PngChunkType type, Span<byte> data) private void WriteChunk(Stream stream, PngChunkType type, ReadOnlySpan<byte> data)
=> this.WriteChunk(stream, type, data, 0, data.Length); => this.WriteChunk(stream, type, data, 0, data.Length);
/// <summary> /// <summary>
@ -1552,7 +1552,7 @@ internal sealed class PngEncoderCore : IDisposable
/// <param name="data">The <see cref="Span{Byte}"/> containing data.</param> /// <param name="data">The <see cref="Span{Byte}"/> containing data.</param>
/// <param name="offset">The position to offset the data at.</param> /// <param name="offset">The position to offset the data at.</param>
/// <param name="length">The of the data to write.</param> /// <param name="length">The of the data to write.</param>
private void WriteChunk(Stream stream, PngChunkType type, Span<byte> data, int offset, int length) private void WriteChunk(Stream stream, PngChunkType type, ReadOnlySpan<byte> data, int offset, int length)
{ {
Span<byte> buffer = stackalloc byte[8]; Span<byte> buffer = stackalloc byte[8];
@ -1584,7 +1584,7 @@ internal sealed class PngEncoderCore : IDisposable
/// <param name="data">The <see cref="Span{Byte}"/> containing data.</param> /// <param name="data">The <see cref="Span{Byte}"/> containing data.</param>
/// <param name="offset">The position to offset the data at.</param> /// <param name="offset">The position to offset the data at.</param>
/// <param name="length">The of the data to write.</param> /// <param name="length">The of the data to write.</param>
private void WriteFrameDataChunk(Stream stream, uint sequenceNumber, Span<byte> data, int offset, int length) private void WriteFrameDataChunk(Stream stream, uint sequenceNumber, ReadOnlySpan<byte> data, int offset, int length)
{ {
Span<byte> buffer = stackalloc byte[12]; Span<byte> buffer = stackalloc byte[12];

6
src/ImageSharp/Formats/Tga/TgaEncoderCore.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
@ -268,7 +268,7 @@ internal sealed class TgaEncoderCore
/// <param name="pixelRow">A pixel row of the image to encode.</param> /// <param name="pixelRow">A pixel row of the image to encode.</param>
/// <param name="xStart">X coordinate to start searching for the same pixels.</param> /// <param name="xStart">X coordinate to start searching for the same pixels.</param>
/// <returns>The number of equal pixels.</returns> /// <returns>The number of equal pixels.</returns>
private static byte FindEqualPixels<TPixel>(Span<TPixel> pixelRow, int xStart) private static byte FindEqualPixels<TPixel>(ReadOnlySpan<TPixel> pixelRow, int xStart)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
byte equalPixelCount = 0; byte equalPixelCount = 0;
@ -301,7 +301,7 @@ internal sealed class TgaEncoderCore
/// <param name="pixelRow">A pixel row of the image to encode.</param> /// <param name="pixelRow">A pixel row of the image to encode.</param>
/// <param name="xStart">X coordinate to start searching for the unequal pixels.</param> /// <param name="xStart">X coordinate to start searching for the unequal pixels.</param>
/// <returns>The number of equal pixels.</returns> /// <returns>The number of equal pixels.</returns>
private static byte FindUnEqualPixels<TPixel>(Span<TPixel> pixelRow, int xStart) private static byte FindUnEqualPixels<TPixel>(ReadOnlySpan<TPixel> pixelRow, int xStart)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
byte unEqualPixelCount = 0; byte unEqualPixelCount = 0;

4
src/ImageSharp/Formats/Tiff/Compression/Compressors/T6BitCompressor.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable #nullable disable
@ -148,7 +148,7 @@ internal sealed class T6BitCompressor : TiffCcittCompressor
/// <returns>The index of the first pixel at or after <paramref name="startIndex"/> /// <returns>The index of the first pixel at or after <paramref name="startIndex"/>
/// that does not match <paramref name="color"/>, or the length of <paramref name="row"/>, /// that does not match <paramref name="color"/>, or the length of <paramref name="row"/>,
/// whichever comes first.</returns> /// whichever comes first.</returns>
private static uint FindRunEnd(Span<byte> row, uint startIndex, byte? color = null) private static uint FindRunEnd(ReadOnlySpan<byte> row, uint startIndex, byte? color = null)
{ {
if (startIndex >= row.Length) if (startIndex >= row.Length)
{ {

6
src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
@ -104,7 +104,7 @@ internal sealed class TiffLzwEncoder : IDisposable
/// </summary> /// </summary>
/// <param name="data">The data to compress.</param> /// <param name="data">The data to compress.</param>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
public void Encode(Span<byte> data, Stream stream) public void Encode(ReadOnlySpan<byte> data, Stream stream)
{ {
this.Reset(); this.Reset();
@ -214,7 +214,7 @@ internal sealed class TiffLzwEncoder : IDisposable
this.bufferPosition = 0; this.bufferPosition = 0;
} }
private byte ReadNextByte(Span<byte> data) => data[this.bufferPosition++]; private byte ReadNextByte(ReadOnlySpan<byte> data) => data[this.bufferPosition++];
private void IncreaseCodeSizeOrResetIfNeeded(Stream stream) private void IncreaseCodeSizeOrResetIfNeeded(Stream stream)
{ {

18
src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
#nullable disable #nullable disable
@ -440,8 +440,8 @@ internal class TiffDecoderCore : ImageDecoderCore
int width, int width,
int height, int height,
int rowsPerStrip, int rowsPerStrip,
Span<ulong> stripOffsets, ReadOnlySpan<ulong> stripOffsets,
Span<ulong> stripByteCounts, ReadOnlySpan<ulong> stripByteCounts,
CancellationToken cancellationToken) CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -518,8 +518,8 @@ internal class TiffDecoderCore : ImageDecoderCore
int width, int width,
int height, int height,
int rowsPerStrip, int rowsPerStrip,
Span<ulong> stripOffsets, ReadOnlySpan<ulong> stripOffsets,
Span<ulong> stripByteCounts, ReadOnlySpan<ulong> stripByteCounts,
CancellationToken cancellationToken) CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -670,8 +670,8 @@ internal class TiffDecoderCore : ImageDecoderCore
int tileLength, int tileLength,
int tilesAcross, int tilesAcross,
int tilesDown, int tilesDown,
Span<ulong> tileOffsets, ReadOnlySpan<ulong> tileOffsets,
Span<ulong> tileByteCounts, ReadOnlySpan<ulong> tileByteCounts,
CancellationToken cancellationToken) CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -780,8 +780,8 @@ internal class TiffDecoderCore : ImageDecoderCore
int tileLength, int tileLength,
int tilesAcross, int tilesAcross,
int tilesDown, int tilesDown,
Span<ulong> tileOffsets, ReadOnlySpan<ulong> tileOffsets,
Span<ulong> tileByteCounts, ReadOnlySpan<ulong> tileByteCounts,
CancellationToken cancellationToken) CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {

4
src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers.Binary; using System.Buffers.Binary;
@ -103,7 +103,7 @@ internal sealed class TiffStreamWriter : IDisposable
/// Writes an array of bytes to the current stream, padded to four-bytes. /// Writes an array of bytes to the current stream, padded to four-bytes.
/// </summary> /// </summary>
/// <param name="value">The bytes to write.</param> /// <param name="value">The bytes to write.</param>
public void WritePadded(Span<byte> value) public void WritePadded(ReadOnlySpan<byte> value)
{ {
this.BaseStream.Write(value); this.BaseStream.Write(value);

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
@ -275,7 +275,7 @@ internal class AlphaDecoder : IDisposable
Vp8LTransform transform, Vp8LTransform transform,
int yStart, int yStart,
int yEnd, int yEnd,
Span<byte> src, ReadOnlySpan<byte> src,
Span<byte> dst) Span<byte> dst)
{ {
int bitsPerPixel = 8 >> transform.Bits; int bitsPerPixel = 8 >> transform.Bits;
@ -311,7 +311,7 @@ internal class AlphaDecoder : IDisposable
} }
} }
private static void HorizontalUnfilter(Span<byte> prev, Span<byte> input, Span<byte> dst, int width) private static void HorizontalUnfilter(ReadOnlySpan<byte> prev, Span<byte> input, Span<byte> dst, int width)
{ {
if (Vector128.IsHardwareAccelerated && width >= 9) if (Vector128.IsHardwareAccelerated && width >= 9)
{ {
@ -368,7 +368,7 @@ internal class AlphaDecoder : IDisposable
} }
} }
private static void GradientUnfilter(Span<byte> prev, Span<byte> input, Span<byte> dst, int width) private static void GradientUnfilter(ReadOnlySpan<byte> prev, Span<byte> input, Span<byte> dst, int width)
{ {
if (prev.IsEmpty) if (prev.IsEmpty)
{ {
@ -424,7 +424,7 @@ internal class AlphaDecoder : IDisposable
return true; return true;
} }
private static void MapAlpha(Span<byte> src, Span<uint> colorMap, Span<byte> dst, int yStart, int yEnd, int width) private static void MapAlpha(ReadOnlySpan<byte> src, ReadOnlySpan<uint> colorMap, Span<byte> dst, int yStart, int yEnd, int width)
{ {
int offset = 0; int offset = 0;
for (int y = yStart; y < yEnd; y++) for (int y = yStart; y < yEnd; y++)
@ -448,7 +448,7 @@ internal class AlphaDecoder : IDisposable
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void ExtractGreen(Span<uint> argb, Span<byte> alpha, int size) private static void ExtractGreen(ReadOnlySpan<uint> argb, Span<byte> alpha, int size)
{ {
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {

4
src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Webp.Chunks; using SixLabors.ImageSharp.Formats.Webp.Chunks;
@ -177,7 +177,7 @@ internal abstract class BitWriterBase
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
/// <param name="dataBytes">The alpha channel data bytes.</param> /// <param name="dataBytes">The alpha channel data bytes.</param>
/// <param name="alphaDataIsCompressed">Indicates, if the alpha channel data is compressed.</param> /// <param name="alphaDataIsCompressed">Indicates, if the alpha channel data is compressed.</param>
public static void WriteAlphaChunk(Stream stream, Span<byte> dataBytes, bool alphaDataIsCompressed) public static void WriteAlphaChunk(Stream stream, ReadOnlySpan<byte> dataBytes, bool alphaDataIsCompressed)
{ {
long pos = RiffHelper.BeginWriteChunk(stream, (uint)WebpChunkType.Alpha); long pos = RiffHelper.BeginWriteChunk(stream, (uint)WebpChunkType.Alpha);
byte flags = 0; byte flags = 0;

4
src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers.Binary; using System.Buffers.Binary;
@ -223,7 +223,7 @@ internal class Vp8BitWriter : BitWriterBase
} }
} }
public int PutI4Mode(int mode, Span<byte> prob) public int PutI4Mode(int mode, ReadOnlySpan<byte> prob)
{ {
if (this.PutBit(mode != B_DC_PRED, prob[0])) if (this.PutBit(mode != B_DC_PRED, prob[0]))
{ {

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
@ -375,7 +375,7 @@ internal static class BackwardReferenceEncoder
return chosenPathSize; return chosenPathSize;
} }
private static void BackwardReferencesHashChainFollowChosenPath(ReadOnlySpan<uint> bgra, int cacheBits, Span<ushort> chosenPath, int chosenPathSize, Vp8LHashChain hashChain, Vp8LBackwardRefs backwardRefs) private static void BackwardReferencesHashChainFollowChosenPath(ReadOnlySpan<uint> bgra, int cacheBits, ReadOnlySpan<ushort> chosenPath, int chosenPathSize, Vp8LHashChain hashChain, Vp8LBackwardRefs backwardRefs)
{ {
bool useColorCache = cacheBits > 0; bool useColorCache = cacheBits > 0;
ColorCache? colorCache = null; ColorCache? colorCache = null;

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

@ -17,7 +17,7 @@ internal static class ColorSpaceTransformUtils
const int span = 16; const int span = 16;
Span<ushort> values = stackalloc ushort[span]; Span<ushort> values = stackalloc ushort[span];
// These shuffle masks are safe for use with Avx2.Shuffle because all indices are within their respective 128-bit lanes (0–15 for the low mask, 16–31 for the high mask), // These shuffle masks are safe for use with Avx2.Shuffle because all indices are within their respective 128-bit lanes (0-15 for the low mask, 16-31 for the high mask),
// and all disabled lanes are set to 0xFF to zero those bytes per the vpshufb specification. This guarantees lane-local shuffling with no cross-lane violations. // and all disabled lanes are set to 0xFF to zero those bytes per the vpshufb specification. This guarantees lane-local shuffling with no cross-lane violations.
Vector256<byte> collectColorBlueTransformsShuffleLowMask256 = Vector256.Create(255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255, 255, 18, 255, 22, 255, 26, 255, 30, 255, 255, 255, 255, 255, 255, 255, 255); Vector256<byte> collectColorBlueTransformsShuffleLowMask256 = Vector256.Create(255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255, 255, 18, 255, 22, 255, 26, 255, 30, 255, 255, 255, 255, 255, 255, 255, 255);
Vector256<byte> collectColorBlueTransformsShuffleHighMask256 = Vector256.Create(255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255, 255, 18, 255, 22, 255, 26, 255, 30); Vector256<byte> 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);
@ -121,7 +121,7 @@ internal static class ColorSpaceTransformUtils
} }
} }
private static void CollectColorBlueTransformsScalar(Span<uint> bgra, int stride, int tileWidth, int tileHeight, int greenToBlue, int redToBlue, Span<int> histo) private static void CollectColorBlueTransformsScalar(ReadOnlySpan<uint> bgra, int stride, int tileWidth, int tileHeight, int greenToBlue, int redToBlue, Span<int> histo)
{ {
int pos = 0; int pos = 0;
while (tileHeight-- > 0) while (tileHeight-- > 0)
@ -230,7 +230,7 @@ internal static class ColorSpaceTransformUtils
} }
} }
private static void CollectColorRedTransformsScalar(Span<uint> bgra, int stride, int tileWidth, int tileHeight, int greenToRed, Span<int> histo) private static void CollectColorRedTransformsScalar(ReadOnlySpan<uint> bgra, int stride, int tileWidth, int tileHeight, int greenToRed, Span<int> histo)
{ {
int pos = 0; int pos = 0;
while (tileHeight-- > 0) while (tileHeight-- > 0)

2
src/ImageSharp/Formats/Webp/Lossless/CostModel.cs

@ -74,7 +74,7 @@ internal class CostModel
public double GetLiteralCost(uint v) => this.Alpha[v >> 24] + this.Red[(v >> 16) & 0xff] + this.Literal[(v >> 8) & 0xff] + this.Blue[v & 0xff]; public double GetLiteralCost(uint v) => this.Alpha[v >> 24] + this.Red[(v >> 16) & 0xff] + this.Literal[(v >> 8) & 0xff] + this.Blue[v & 0xff];
private static void ConvertPopulationCountTableToBitEstimates(int numSymbols, Span<uint> populationCounts, double[] output) private static void ConvertPopulationCountTableToBitEstimates(int numSymbols, ReadOnlySpan<uint> populationCounts, double[] output)
{ {
uint sum = 0; uint sum = 0;
int nonzeros = 0; int nonzeros = 0;

2
src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs

@ -196,7 +196,7 @@ internal static class HistogramEncoder
private static void HistogramCombineEntropyBin( private static void HistogramCombineEntropyBin(
Vp8LHistogramSet histograms, Vp8LHistogramSet histograms,
Span<ushort> clusters, ReadOnlySpan<ushort> clusters,
Span<ushort> clusterMappings, Span<ushort> clusterMappings,
Vp8LHistogram curCombo, Vp8LHistogram curCombo,
ReadOnlySpan<ushort> binMap, ReadOnlySpan<ushort> binMap,

8
src/ImageSharp/Formats/Webp/Lossless/HuffmanUtils.cs

@ -159,7 +159,7 @@ internal static class HuffmanUtils
/// <param name="histogramSize">The size of the histogram.</param> /// <param name="histogramSize">The size of the histogram.</param>
/// <param name="treeDepthLimit">The tree depth limit.</param> /// <param name="treeDepthLimit">The tree depth limit.</param>
/// <param name="bitDepths">How many bits are used for the symbol.</param> /// <param name="bitDepths">How many bits are used for the symbol.</param>
public static void GenerateOptimalTree(Span<HuffmanTree> tree, Span<uint> histogram, int histogramSize, int treeDepthLimit, byte[] bitDepths) public static void GenerateOptimalTree(Span<HuffmanTree> tree, ReadOnlySpan<uint> histogram, int histogramSize, int treeDepthLimit, byte[] bitDepths)
{ {
uint countMin; uint countMin;
int treeSizeOrig = 0; int treeSizeOrig = 0;
@ -445,7 +445,7 @@ internal static class HuffmanUtils
return totalSize; return totalSize;
} }
private static int CodeRepeatedZeros(int repetitions, Span<HuffmanTreeToken> tokens) private static int CodeRepeatedZeros(int repetitions, ReadOnlySpan<HuffmanTreeToken> tokens)
{ {
int pos = 0; int pos = 0;
while (repetitions >= 1) while (repetitions >= 1)
@ -487,7 +487,7 @@ internal static class HuffmanUtils
return pos; return pos;
} }
private static int CodeRepeatedValues(int repetitions, Span<HuffmanTreeToken> tokens, int value, int prevValue) private static int CodeRepeatedValues(int repetitions, ReadOnlySpan<HuffmanTreeToken> tokens, int value, int prevValue)
{ {
int pos = 0; int pos = 0;
@ -565,7 +565,7 @@ internal static class HuffmanUtils
} }
} }
private static void SetBitDepths(Span<HuffmanTree> tree, Span<HuffmanTree> pool, byte[] bitDepths, int level) private static void SetBitDepths(ReadOnlySpan<HuffmanTree> tree, Span<HuffmanTree> pool, byte[] bitDepths, int level)
{ {
if (tree[0].PoolIndexLeft >= 0) if (tree[0].PoolIndexLeft >= 0)
{ {

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

@ -98,7 +98,7 @@ internal static unsafe class LosslessUtils
if (Vector256.IsHardwareAccelerated && pixelData.Length >= 8) if (Vector256.IsHardwareAccelerated && pixelData.Length >= 8)
{ {
// The `255` values disable the write for alpha (A), since 0x80 is set in the control byte (high bit set). // The `255` values disable the write for alpha (A), since 0x80 is set in the control byte (high bit set).
// Each byte index is within its respective 128-bit lane (0–15 and 16–31), so this is safe for per-lane shuffle. // Each byte index is within its respective 128-bit lane (0-15 and 16-31), so this is safe for per-lane shuffle.
// The high bits are not set for the index bytes, and the values are always < 16 per lane, satisfying AVX2 lane rules. // The high bits are not set for the index bytes, and the values are always < 16 per lane, satisfying AVX2 lane rules.
Vector256<byte> addGreenToBlueAndRedMask = 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); Vector256<byte> addGreenToBlueAndRedMask = 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);
nuint numPixels = (uint)pixelData.Length; nuint numPixels = (uint)pixelData.Length;
@ -675,7 +675,7 @@ internal static unsafe class LosslessUtils
/// <summary> /// <summary>
/// Bundles multiple (1, 2, 4 or 8) pixels into a single pixel. /// Bundles multiple (1, 2, 4 or 8) pixels into a single pixel.
/// </summary> /// </summary>
public static void BundleColorMap(Span<byte> row, int width, int xBits, Span<uint> dst) public static void BundleColorMap(ReadOnlySpan<byte> row, int width, int xBits, Span<uint> dst)
{ {
int x; int x;
if (xBits > 0) if (xBits > 0)

2
src/ImageSharp/Formats/Webp/Lossless/NearLosslessEnc.cs

@ -99,7 +99,7 @@ internal static class NearLosslessEnc
return biased & ~mask; return biased & ~mask;
} }
private static bool IsSmooth(Span<uint> prevRow, Span<uint> currRow, Span<uint> nextRow, int ix, int limit) => private static bool IsSmooth(ReadOnlySpan<uint> prevRow, ReadOnlySpan<uint> currRow, ReadOnlySpan<uint> nextRow, int ix, int limit) =>
IsNear(currRow[ix], currRow[ix - 1], limit) && // Check that all pixels in 4-connected neighborhood are smooth. IsNear(currRow[ix], currRow[ix - 1], limit) && // Check that all pixels in 4-connected neighborhood are smooth.
IsNear(currRow[ix], currRow[ix + 1], limit) && IsNear(currRow[ix], currRow[ix + 1], limit) &&
IsNear(currRow[ix], prevRow[ix], limit) && IsNear(currRow[ix], prevRow[ix], limit) &&

10
src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs

@ -205,7 +205,7 @@ internal static unsafe class PredictorEncoder
TransparentColorMode transparentColorMode, TransparentColorMode transparentColorMode,
bool usedSubtractGreen, bool usedSubtractGreen,
bool nearLossless, bool nearLossless,
Span<uint> modes, ReadOnlySpan<uint> modes,
Span<short> scratch) Span<short> scratch)
{ {
const int numPredModes = 14; const int numPredModes = 14;
@ -334,7 +334,7 @@ internal static unsafe class PredictorEncoder
int height, int height,
Span<uint> upperRowSpan, Span<uint> upperRowSpan,
Span<uint> currentRowSpan, Span<uint> currentRowSpan,
Span<byte> maxDiffs, ReadOnlySpan<byte> maxDiffs,
int mode, int mode,
int xStart, int xStart,
int xEnd, int xEnd,
@ -575,7 +575,7 @@ internal static unsafe class PredictorEncoder
int width, int width,
int height, int height,
int bits, int bits,
Span<uint> modes, ReadOnlySpan<uint> modes,
Span<uint> argbScratch, Span<uint> argbScratch,
Span<uint> argb, Span<uint> argb,
int maxQuantization, int maxQuantization,
@ -746,7 +746,7 @@ internal static unsafe class PredictorEncoder
} }
#pragma warning restore SA1503 // Braces should not be omitted #pragma warning restore SA1503 // Braces should not be omitted
private static void MaxDiffsForRow(int width, int stride, Span<uint> argb, int offset, Span<byte> maxDiffs, bool usedSubtractGreen) private static void MaxDiffsForRow(int width, int stride, ReadOnlySpan<uint> argb, int offset, Span<byte> maxDiffs, bool usedSubtractGreen)
{ {
if (width <= 2) if (width <= 2)
{ {
@ -1058,7 +1058,7 @@ internal static unsafe class PredictorEncoder
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static float PredictionCostSpatial(Span<int> counts, int weight0, double expVal) private static float PredictionCostSpatial(ReadOnlySpan<int> counts, int weight0, double expVal)
{ {
int significantSymbols = 256 >> 4; int significantSymbols = 256 >> 4;
double expDecayFactor = 0.6; double expDecayFactor = 0.6;

8
src/ImageSharp/Formats/Webp/Lossless/Vp8LBitEntropy.cs

@ -100,7 +100,7 @@ internal class Vp8LBitEntropy
return this.Entropy < minLimit ? minLimit : this.Entropy; return this.Entropy < minLimit ? minLimit : this.Entropy;
} }
public void BitsEntropyUnrefined(Span<uint> array, int n) public void BitsEntropyUnrefined(ReadOnlySpan<uint> array, int n)
{ {
this.Init(); this.Init();
@ -125,7 +125,7 @@ internal class Vp8LBitEntropy
/// <summary> /// <summary>
/// Get the entropy for the distribution 'X'. /// Get the entropy for the distribution 'X'.
/// </summary> /// </summary>
public void BitsEntropyUnrefined(Span<uint> x, int length, Vp8LStreaks stats) public void BitsEntropyUnrefined(ReadOnlySpan<uint> x, int length, Vp8LStreaks stats)
{ {
int i; int i;
int iPrev = 0; int iPrev = 0;
@ -147,7 +147,7 @@ internal class Vp8LBitEntropy
this.Entropy += LosslessUtils.FastSLog2(this.Sum); this.Entropy += LosslessUtils.FastSLog2(this.Sum);
} }
public void GetCombinedEntropyUnrefined(Span<uint> x, Span<uint> y, int length, Vp8LStreaks stats) public void GetCombinedEntropyUnrefined(ReadOnlySpan<uint> x, ReadOnlySpan<uint> y, int length, Vp8LStreaks stats)
{ {
int i; int i;
int iPrev = 0; int iPrev = 0;
@ -169,7 +169,7 @@ internal class Vp8LBitEntropy
this.Entropy += LosslessUtils.FastSLog2(this.Sum); this.Entropy += LosslessUtils.FastSLog2(this.Sum);
} }
public void GetEntropyUnrefined(Span<uint> x, int length, Vp8LStreaks stats) public void GetEntropyUnrefined(ReadOnlySpan<uint> x, int length, Vp8LStreaks stats)
{ {
int i; int i;
int iPrev = 0; int iPrev = 0;

16
src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

@ -812,7 +812,7 @@ internal class Vp8LEncoder : IDisposable
this.EncodeImageNoHuffman(this.TransformData.GetSpan(), this.HashChain, this.Refs[0], this.Refs[1], transformWidth, transformHeight, this.quality, lowEffort); this.EncodeImageNoHuffman(this.TransformData.GetSpan(), this.HashChain, this.Refs[0], this.Refs[1], transformWidth, transformHeight, this.quality, lowEffort);
} }
private void EncodeImageNoHuffman(Span<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs refsTmp1, Vp8LBackwardRefs refsTmp2, int width, int height, uint quality, bool lowEffort) private void EncodeImageNoHuffman(ReadOnlySpan<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs refsTmp1, Vp8LBackwardRefs refsTmp2, int width, int height, uint quality, bool lowEffort)
{ {
int cacheBits = 0; int cacheBits = 0;
ushort[] histogramSymbols = new ushort[1]; // Only one tree, one symbol. ushort[] histogramSymbols = new ushort[1]; // Only one tree, one symbol.
@ -1044,7 +1044,7 @@ internal class Vp8LEncoder : IDisposable
int width, int width,
int histoBits, int histoBits,
Vp8LBackwardRefs backwardRefs, Vp8LBackwardRefs backwardRefs,
Span<ushort> histogramSymbols, ReadOnlySpan<ushort> histogramSymbols,
HuffmanTreeCode[] huffmanCodes) HuffmanTreeCode[] huffmanCodes)
{ {
int histoXSize = histoBits > 0 ? LosslessUtils.SubSampleSize(width, histoBits) : 1; int histoXSize = histoBits > 0 ? LosslessUtils.SubSampleSize(width, histoBits) : 1;
@ -1457,7 +1457,7 @@ internal class Vp8LEncoder : IDisposable
} }
} }
private static void ApplyPaletteFor(int width, int height, Span<uint> palette, int hashIdx, Span<uint> src, int srcStride, Span<uint> dst, int dstStride, Span<byte> tmpRow, uint[] buffer, int xBits) private static void ApplyPaletteFor(int width, int height, ReadOnlySpan<uint> palette, int hashIdx, Span<uint> src, int srcStride, Span<uint> dst, int dstStride, Span<byte> tmpRow, uint[] buffer, int xBits)
{ {
uint prevPix = palette[0]; uint prevPix = palette[0];
uint prevIdx = 0; uint prevIdx = 0;
@ -1494,7 +1494,7 @@ internal class Vp8LEncoder : IDisposable
} }
} }
private static void ApplyPaletteForWithIdxMap(int width, int height, Span<uint> palette, Span<uint> src, int srcStride, Span<uint> dst, int dstStride, Span<byte> tmpRow, uint[] idxMap, int xBits, uint[] paletteSorted, int paletteSize) private static void ApplyPaletteForWithIdxMap(int width, int height, ReadOnlySpan<uint> palette, Span<uint> src, int srcStride, Span<uint> dst, int dstStride, Span<byte> tmpRow, uint[] idxMap, int xBits, uint[] paletteSorted, int paletteSize)
{ {
uint prevPix = palette[0]; uint prevPix = palette[0];
uint prevIdx = 0; uint prevIdx = 0;
@ -1590,7 +1590,7 @@ internal class Vp8LEncoder : IDisposable
/// <param name="palette">The palette.</param> /// <param name="palette">The palette.</param>
/// <param name="numColors">Number of colors in the palette.</param> /// <param name="numColors">Number of colors in the palette.</param>
/// <returns>True, if the palette has no monotonous deltas.</returns> /// <returns>True, if the palette has no monotonous deltas.</returns>
private static bool PaletteHasNonMonotonousDeltas(Span<uint> palette, int numColors) private static bool PaletteHasNonMonotonousDeltas(ReadOnlySpan<uint> palette, int numColors)
{ {
const uint predict = 0x000000; const uint predict = 0x000000;
byte signFound = 0x00; byte signFound = 0x00;
@ -1619,6 +1619,7 @@ internal class Vp8LEncoder : IDisposable
return (signFound & (signFound << 1)) != 0; // two consequent signs. return (signFound & (signFound << 1)) != 0; // two consequent signs.
} }
#pragma warning disable CA1517 // False positive: https://github.com/dotnet/sdk/issues/53388
/// <summary> /// <summary>
/// Find greedily always the closest color of the predicted color to minimize /// Find greedily always the closest color of the predicted color to minimize
/// deltas in the palette. This reduces storage needs since the palette is stored with delta encoding. /// deltas in the palette. This reduces storage needs since the palette is stored with delta encoding.
@ -1647,6 +1648,7 @@ internal class Vp8LEncoder : IDisposable
predict = palette[i]; predict = palette[i];
} }
} }
#pragma warning restore CA1517
private static void GetHuffBitLengthsAndCodes(Vp8LHistogramSet histogramImage, HuffmanTreeCode[] huffmanCodes) private static void GetHuffBitLengthsAndCodes(Vp8LHistogramSet histogramImage, HuffmanTreeCode[] huffmanCodes)
{ {
@ -1762,7 +1764,7 @@ internal class Vp8LEncoder : IDisposable
/// <summary> /// <summary>
/// Bundles multiple (1, 2, 4 or 8) pixels into a single pixel. /// Bundles multiple (1, 2, 4 or 8) pixels into a single pixel.
/// </summary> /// </summary>
private static void BundleColorMap(Span<byte> row, int width, int xBits, Span<uint> dst) private static void BundleColorMap(ReadOnlySpan<byte> row, int width, int xBits, Span<uint> dst)
{ {
int x; int x;
if (xBits > 0) if (xBits > 0)
@ -1836,7 +1838,7 @@ internal class Vp8LEncoder : IDisposable
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint SearchColorGreedy(Span<uint> palette, uint color) private static uint SearchColorGreedy(ReadOnlySpan<uint> palette, uint color)
{ {
if (color == palette[0]) if (color == palette[0])
{ {

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

@ -441,8 +441,8 @@ internal abstract unsafe class Vp8LHistogram
} }
private static double GetCombinedEntropy( private static double GetCombinedEntropy(
Span<uint> x, ReadOnlySpan<uint> x,
Span<uint> y, ReadOnlySpan<uint> y,
int length, int length,
bool isXUsed, bool isXUsed,
bool isYUsed, bool isYUsed,
@ -494,7 +494,7 @@ internal abstract unsafe class Vp8LHistogram
return bitEntropy.BitsEntropyRefine() + stats.FinalHuffmanCost(); return bitEntropy.BitsEntropyRefine() + stats.FinalHuffmanCost();
} }
private static double ExtraCostCombined(Span<uint> x, Span<uint> y, int length) private static double ExtraCostCombined(ReadOnlySpan<uint> x, ReadOnlySpan<uint> y, int length)
{ {
double cost = 0.0d; double cost = 0.0d;
for (int i = 2; i < length - 2; i++) for (int i = 2; i < length - 2; i++)
@ -509,7 +509,7 @@ internal abstract unsafe class Vp8LHistogram
/// <summary> /// <summary>
/// Get the symbol entropy for the distribution 'population'. /// Get the symbol entropy for the distribution 'population'.
/// </summary> /// </summary>
private double PopulationCost(Span<uint> population, int length, ref uint trivialSym, int isUsedIndex, Vp8LStreaks stats, Vp8LBitEntropy bitEntropy) private double PopulationCost(ReadOnlySpan<uint> population, int length, ref uint trivialSym, int isUsedIndex, Vp8LStreaks stats, Vp8LBitEntropy bitEntropy)
{ {
bitEntropy.Init(); bitEntropy.Init();
stats.Clear(); stats.Clear();
@ -523,7 +523,7 @@ internal abstract unsafe class Vp8LHistogram
return bitEntropy.BitsEntropyRefine() + stats.FinalHuffmanCost(); return bitEntropy.BitsEntropyRefine() + stats.FinalHuffmanCost();
} }
private static double ExtraCost(Span<uint> population, int length) private static double ExtraCost(ReadOnlySpan<uint> population, int length)
{ {
double cost = 0.0d; double cost = 0.0d;
for (int i = 2; i < length - 2; i++) for (int i = 2; i < length - 2; i++)

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

@ -339,7 +339,7 @@ internal sealed class WebpLosslessDecoder
} }
} }
private static void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels, Span<uint> pixelData, ref int lastCached) private static void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels, ReadOnlySpan<uint> pixelData, ref int lastCached)
{ {
col++; col++;
decodedPixels++; decodedPixels++;
@ -826,7 +826,7 @@ internal sealed class WebpLosslessDecoder
decoder.Metadata.HuffmanMask = numBits == 0 ? ~0 : (1 << numBits) - 1; decoder.Metadata.HuffmanMask = numBits == 0 ? ~0 : (1 << numBits) - 1;
} }
private uint ReadPackedSymbols(Span<HTreeGroup> group, Span<uint> pixelData, int decodedPixels) private uint ReadPackedSymbols(ReadOnlySpan<HTreeGroup> group, Span<uint> pixelData, int decodedPixels)
{ {
uint val = (uint)(this.bitReader.PrefetchBits() & (HuffmanUtils.HuffmanPackedTableSize - 1)); uint val = (uint)(this.bitReader.PrefetchBits() & (HuffmanUtils.HuffmanPackedTableSize - 1));
HuffmanCode code = group[0].PackedTable[val]; HuffmanCode code = group[0].PackedTable[val];

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

@ -123,7 +123,7 @@ internal static class LossyUtils
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8_SseNxN(Span<byte> a, Span<byte> b, int w, int h) public static int Vp8_SseNxN(ReadOnlySpan<byte> a, ReadOnlySpan<byte> b, int w, int h)
{ {
int count = 0; int count = 0;
int offset = 0; int offset = 0;
@ -285,7 +285,7 @@ internal static class LossyUtils
return Math.Abs(sum2 - sum1) >> 5; return Math.Abs(sum2 - sum1) >> 5;
} }
public static void DC16(Span<byte> dst, Span<byte> yuv, int offset) public static void DC16(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
int offsetMinus1 = offset - 1; int offsetMinus1 = offset - 1;
int offsetMinusBps = offset - WebpConstants.Bps; int offsetMinusBps = offset - WebpConstants.Bps;
@ -313,7 +313,7 @@ internal static class LossyUtils
} }
} }
public static void HE16(Span<byte> dst, Span<byte> yuv, int offset) public static void HE16(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
// horizontal // horizontal
offset--; offset--;
@ -327,7 +327,7 @@ internal static class LossyUtils
} }
} }
public static void DC16NoTop(Span<byte> dst, Span<byte> yuv, int offset) public static void DC16NoTop(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
// DC with top samples not available. // DC with top samples not available.
int dc = 8; int dc = 8;
@ -340,7 +340,7 @@ internal static class LossyUtils
Put16(dc >> 4, dst); Put16(dc >> 4, dst);
} }
public static void DC16NoLeft(Span<byte> dst, Span<byte> yuv, int offset) public static void DC16NoLeft(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
// DC with left samples not available. // DC with left samples not available.
int dc = 8; int dc = 8;
@ -357,7 +357,7 @@ internal static class LossyUtils
public static void DC16NoTopLeft(Span<byte> dst) => public static void DC16NoTopLeft(Span<byte> dst) =>
Put16(0x80, dst); // DC with no top and left samples. Put16(0x80, dst); // DC with no top and left samples.
public static void DC8uv(Span<byte> dst, Span<byte> yuv, int offset) public static void DC8uv(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
int dc0 = 8; int dc0 = 8;
int offsetMinus1 = offset - 1; int offsetMinus1 = offset - 1;
@ -388,7 +388,7 @@ internal static class LossyUtils
} }
} }
public static void HE8uv(Span<byte> dst, Span<byte> yuv, int offset) public static void HE8uv(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
// horizontal // horizontal
offset--; offset--;
@ -403,7 +403,7 @@ internal static class LossyUtils
} }
} }
public static void DC8uvNoTop(Span<byte> dst, Span<byte> yuv, int offset) public static void DC8uvNoTop(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
// DC with no top samples. // DC with no top samples.
int dc0 = 4; int dc0 = 4;
@ -418,7 +418,7 @@ internal static class LossyUtils
Put8x8uv((byte)(dc0 >> 3), dst); Put8x8uv((byte)(dc0 >> 3), dst);
} }
public static void DC8uvNoLeft(Span<byte> dst, Span<byte> yuv, int offset) public static void DC8uvNoLeft(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
// DC with no left samples. // DC with no left samples.
int offsetMinusBps = offset - WebpConstants.Bps; int offsetMinusBps = offset - WebpConstants.Bps;
@ -436,7 +436,7 @@ internal static class LossyUtils
public static void DC8uvNoTopLeft(Span<byte> dst) => public static void DC8uvNoTopLeft(Span<byte> dst) =>
Put8x8uv(0x80, dst); // DC with nothing. Put8x8uv(0x80, dst); // DC with nothing.
public static void DC4(Span<byte> dst, Span<byte> yuv, int offset) public static void DC4(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
int dc = 4; int dc = 4;
int offsetMinusBps = offset - WebpConstants.Bps; int offsetMinusBps = offset - WebpConstants.Bps;
@ -457,7 +457,7 @@ internal static class LossyUtils
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void TM4(Span<byte> dst, Span<byte> yuv, int offset) => TrueMotion(dst, yuv, offset, 4); public static void TM4(Span<byte> dst, Span<byte> yuv, int offset) => TrueMotion(dst, yuv, offset, 4);
public static void VE4(Span<byte> dst, Span<byte> yuv, int offset, Span<byte> vals) public static void VE4(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset, Span<byte> vals)
{ {
// vertical // vertical
int topOffset = offset - WebpConstants.Bps; int topOffset = offset - WebpConstants.Bps;
@ -472,7 +472,7 @@ internal static class LossyUtils
} }
} }
public static void HE4(Span<byte> dst, Span<byte> yuv, int offset) public static void HE4(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
// horizontal // horizontal
int offsetMinusOne = offset - 1; int offsetMinusOne = offset - 1;
@ -491,7 +491,7 @@ internal static class LossyUtils
BinaryPrimitives.WriteUInt32BigEndian(dst[(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, ReadOnlySpan<byte> yuv, int offset)
{ {
// Down-right // Down-right
int offsetMinusOne = offset - 1; int offsetMinusOne = offset - 1;
@ -528,7 +528,7 @@ internal static class LossyUtils
Dst(dst, 3, 0, Avg3(d, c, b)); Dst(dst, 3, 0, Avg3(d, c, b));
} }
public static void VR4(Span<byte> dst, Span<byte> yuv, int offset) public static void VR4(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
// Vertical-Right // Vertical-Right
int offsetMinusOne = offset - 1; int offsetMinusOne = offset - 1;
@ -565,7 +565,7 @@ internal static class LossyUtils
Dst(dst, 3, 1, Avg3(b, c, d)); Dst(dst, 3, 1, Avg3(b, c, d));
} }
public static void LD4(Span<byte> dst, Span<byte> yuv, int offset) public static void LD4(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
// Down-Left // Down-Left
byte a = yuv[offset - WebpConstants.Bps]; byte a = yuv[offset - WebpConstants.Bps];
@ -600,7 +600,7 @@ internal static class LossyUtils
Dst(dst, 3, 3, Avg3(g, h, h)); Dst(dst, 3, 3, Avg3(g, h, h));
} }
public static void VL4(Span<byte> dst, Span<byte> yuv, int offset) public static void VL4(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
// Vertical-Left // Vertical-Left
byte a = yuv[offset - WebpConstants.Bps]; byte a = yuv[offset - WebpConstants.Bps];
@ -636,7 +636,7 @@ internal static class LossyUtils
Dst(dst, 3, 3, Avg3(f, g, h)); Dst(dst, 3, 3, Avg3(f, g, h));
} }
public static void HD4(Span<byte> dst, Span<byte> yuv, int offset) public static void HD4(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
// Horizontal-Down // Horizontal-Down
byte i = yuv[offset - 1]; byte i = yuv[offset - 1];
@ -672,7 +672,7 @@ internal static class LossyUtils
Dst(dst, 1, 3, Avg3(l, k, j)); Dst(dst, 1, 3, Avg3(l, k, j));
} }
public static void HU4(Span<byte> dst, Span<byte> yuv, int offset) public static void HU4(Span<byte> dst, ReadOnlySpan<byte> yuv, int offset)
{ {
// Horizontal-Up // Horizontal-Up
byte i = yuv[offset - 1]; byte i = yuv[offset - 1];
@ -705,7 +705,7 @@ internal static class LossyUtils
/// <summary> /// <summary>
/// Paragraph 14.3: Implementation of the Walsh-Hadamard transform inversion. /// Paragraph 14.3: Implementation of the Walsh-Hadamard transform inversion.
/// </summary> /// </summary>
public static void TransformWht(Span<short> input, Span<short> output, Span<int> scratch) public static void TransformWht(ReadOnlySpan<short> input, Span<short> output, Span<int> scratch)
{ {
Span<int> tmp = scratch[..16]; Span<int> tmp = scratch[..16];
tmp.Clear(); tmp.Clear();
@ -746,7 +746,7 @@ internal static class LossyUtils
/// Returns the weighted sum of the absolute value of transformed coefficients. /// Returns the weighted sum of the absolute value of transformed coefficients.
/// w[] contains a row-major 4 by 4 symmetric matrix. /// w[] contains a row-major 4 by 4 symmetric matrix.
/// </summary> /// </summary>
public static int TTransform(Span<byte> input, Span<ushort> w, Span<int> scratch) public static int TTransform(ReadOnlySpan<byte> input, Span<ushort> w, Span<int> scratch)
{ {
int sum = 0; int sum = 0;
Span<int> tmp = scratch[..16]; Span<int> tmp = scratch[..16];
@ -1249,7 +1249,7 @@ internal static class LossyUtils
} }
} }
public static void TransformDc(Span<short> src, Span<byte> dst) public static void TransformDc(ReadOnlySpan<short> src, Span<byte> dst)
{ {
int dc = src[0] + 4; int dc = src[0] + 4;
for (int j = 0; j < 4; j++) for (int j = 0; j < 4; j++)
@ -1262,7 +1262,7 @@ internal static class LossyUtils
} }
// Simplified transform when only src[0], src[1] and src[4] are non-zero // Simplified transform when only src[0], src[1] and src[4] are non-zero
public static void TransformAc3(Span<short> src, Span<byte> dst) public static void TransformAc3(ReadOnlySpan<short> src, Span<byte> dst)
{ {
int a = src[0] + 4; int a = src[0] + 4;
int c4 = Mul2(src[4]); int c4 = Mul2(src[4]);
@ -2133,7 +2133,7 @@ internal static class LossyUtils
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static bool NeedsFilter(Span<byte> p, int offset, int step, int t) private static bool NeedsFilter(ReadOnlySpan<byte> p, int offset, int step, int t)
{ {
int p1 = p[offset + (-2 * step)]; int p1 = p[offset + (-2 * step)];
int p0 = p[offset - step]; int p0 = p[offset - step];
@ -2142,7 +2142,7 @@ internal static class LossyUtils
return (4 * WebpLookupTables.Abs0(p0 - q0)) + WebpLookupTables.Abs0(p1 - q1) <= t; return (4 * WebpLookupTables.Abs0(p0 - q0)) + WebpLookupTables.Abs0(p1 - q1) <= t;
} }
private static bool NeedsFilter2(Span<byte> p, int offset, int step, int t, int it) private static bool NeedsFilter2(ReadOnlySpan<byte> p, int offset, int step, int t, int it)
{ {
int step2 = 2 * step; int step2 = 2 * step;
int step3 = 3 * step; int step3 = 3 * step;
@ -2357,7 +2357,7 @@ internal static class LossyUtils
=> Vector128_.SubtractSaturate(q, p) | Vector128_.SubtractSaturate(p, q); => Vector128_.SubtractSaturate(q, p) | Vector128_.SubtractSaturate(p, q);
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static bool Hev(Span<byte> p, int offset, int step, int thresh) private static bool Hev(ReadOnlySpan<byte> p, int offset, int step, int thresh)
{ {
int p1 = p[offset - (2 * step)]; int p1 = p[offset - (2 * step)];
int p0 = p[offset - step]; int p0 = p[offset - step];

2
src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs

@ -854,7 +854,7 @@ internal class Vp8EncIterator
} }
} }
private static void ImportLine(Span<byte> src, int srcStride, Span<byte> dst, int len, int totalLen) private static void ImportLine(ReadOnlySpan<byte> src, int srcStride, Span<byte> dst, int len, int totalLen)
{ {
int i; int i;
int srcIdx = 0; int srcIdx = 0;

2
src/ImageSharp/Formats/Webp/Lossy/Vp8EncProba.cs

@ -234,7 +234,7 @@ internal class Vp8EncProba
private static int CalcSkipProba(long nb, long total) => (int)(total != 0 ? (total - nb) * 255 / total : 255); private static int CalcSkipProba(long nb, long total) => (int)(total != 0 ? (total - nb) * 255 / total : 255);
private static int VariableLevelCost(int level, Span<byte> probas) private static int VariableLevelCost(int level, ReadOnlySpan<byte> probas)
{ {
int pattern = WebpLookupTables.Vp8LevelCodes[level - 1][0]; int pattern = WebpLookupTables.Vp8LevelCodes[level - 1][0];
int bits = WebpLookupTables.Vp8LevelCodes[level - 1][1]; int bits = WebpLookupTables.Vp8LevelCodes[level - 1][1];

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

@ -599,7 +599,7 @@ internal static unsafe class Vp8Encoding
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();
} }
public static void FTransformWht(Span<short> input, Span<short> output, Span<int> scratch) public static void FTransformWht(ReadOnlySpan<short> input, Span<short> output, Span<int> scratch)
{ {
Span<int> tmp = scratch[..16]; Span<int> tmp = scratch[..16];
@ -679,7 +679,7 @@ internal static unsafe class Vp8Encoding
// 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, ReadOnlySpan<byte> top, int topOffset, Span<byte> vals)
{ {
Dc4(dst[I4DC4..], top, topOffset); Dc4(dst[I4DC4..], top, topOffset);
Tm4(dst[I4TM4..], top, topOffset); Tm4(dst[I4TM4..], top, topOffset);
@ -764,7 +764,7 @@ internal static unsafe class Vp8Encoding
} }
} }
private static void DcMode(Span<byte> dst, Span<byte> left, Span<byte> top, int size, int round, int shift) private static void DcMode(Span<byte> dst, Span<byte> left, ReadOnlySpan<byte> top, int size, int round, int shift)
{ {
int dc = 0; int dc = 0;
int j; int j;
@ -813,7 +813,7 @@ internal static unsafe class Vp8Encoding
Fill(dst, dc, size); Fill(dst, dc, size);
} }
private static void Dc4(Span<byte> dst, Span<byte> top, int topOffset) private static void Dc4(Span<byte> dst, ReadOnlySpan<byte> top, int topOffset)
{ {
uint dc = 4; uint dc = 4;
int i; int i;
@ -825,7 +825,7 @@ internal static unsafe class Vp8Encoding
Fill(dst, (int)(dc >> 3), 4); Fill(dst, (int)(dc >> 3), 4);
} }
private static void Tm4(Span<byte> dst, Span<byte> top, int topOffset) private static void Tm4(Span<byte> dst, ReadOnlySpan<byte> top, int topOffset)
{ {
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++)
@ -840,7 +840,7 @@ internal static unsafe class Vp8Encoding
} }
} }
private static void Ve4(Span<byte> dst, Span<byte> top, int topOffset, Span<byte> vals) private static void Ve4(Span<byte> dst, ReadOnlySpan<byte> top, int topOffset, Span<byte> vals)
{ {
// vertical // vertical
vals[0] = LossyUtils.Avg3(top[topOffset - 1], top[topOffset], top[topOffset + 1]); vals[0] = LossyUtils.Avg3(top[topOffset - 1], top[topOffset], top[topOffset + 1]);
@ -853,7 +853,7 @@ internal static unsafe class Vp8Encoding
} }
} }
private static void He4(Span<byte> dst, Span<byte> top, int topOffset) private static void He4(Span<byte> dst, ReadOnlySpan<byte> top, int topOffset)
{ {
// horizontal // horizontal
byte x = top[topOffset - 1]; byte x = top[topOffset - 1];
@ -872,7 +872,7 @@ internal static unsafe class Vp8Encoding
BinaryPrimitives.WriteUInt32BigEndian(dst[(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, ReadOnlySpan<byte> top, int topOffset)
{ {
byte x = top[topOffset - 1]; byte x = top[topOffset - 1];
byte i = top[topOffset - 2]; byte i = top[topOffset - 2];
@ -907,7 +907,7 @@ internal static unsafe class Vp8Encoding
LossyUtils.Dst(dst, 3, 0, LossyUtils.Avg3(d, c, b)); LossyUtils.Dst(dst, 3, 0, LossyUtils.Avg3(d, c, b));
} }
private static void Vr4(Span<byte> dst, Span<byte> top, int topOffset) private static void Vr4(Span<byte> dst, ReadOnlySpan<byte> top, int topOffset)
{ {
byte x = top[topOffset - 1]; byte x = top[topOffset - 1];
byte i = top[topOffset - 2]; byte i = top[topOffset - 2];
@ -942,7 +942,7 @@ internal static unsafe class Vp8Encoding
LossyUtils.Dst(dst, 3, 1, LossyUtils.Avg3(b, c, d)); LossyUtils.Dst(dst, 3, 1, LossyUtils.Avg3(b, c, d));
} }
private static void Ld4(Span<byte> dst, Span<byte> top, int topOffset) private static void Ld4(Span<byte> dst, ReadOnlySpan<byte> top, int topOffset)
{ {
byte a = top[topOffset + 0]; byte a = top[topOffset + 0];
byte b = top[topOffset + 1]; byte b = top[topOffset + 1];
@ -976,7 +976,7 @@ internal static unsafe class Vp8Encoding
LossyUtils.Dst(dst, 3, 3, LossyUtils.Avg3(g, h, h)); LossyUtils.Dst(dst, 3, 3, LossyUtils.Avg3(g, h, h));
} }
private static void Vl4(Span<byte> dst, Span<byte> top, int topOffset) private static void Vl4(Span<byte> dst, ReadOnlySpan<byte> top, int topOffset)
{ {
byte a = top[topOffset + 0]; byte a = top[topOffset + 0];
byte b = top[topOffset + 1]; byte b = top[topOffset + 1];
@ -1011,7 +1011,7 @@ internal static unsafe class Vp8Encoding
LossyUtils.Dst(dst, 3, 3, LossyUtils.Avg3(f, g, h)); LossyUtils.Dst(dst, 3, 3, LossyUtils.Avg3(f, g, h));
} }
private static void Hd4(Span<byte> dst, Span<byte> top, int topOffset) private static void Hd4(Span<byte> dst, ReadOnlySpan<byte> top, int topOffset)
{ {
byte x = top[topOffset - 1]; byte x = top[topOffset - 1];
byte i = top[topOffset - 2]; byte i = top[topOffset - 2];
@ -1046,7 +1046,7 @@ internal static unsafe class Vp8Encoding
LossyUtils.Dst(dst, 1, 3, LossyUtils.Avg3(l, k, j)); LossyUtils.Dst(dst, 1, 3, LossyUtils.Avg3(l, k, j));
} }
private static void Hu4(Span<byte> dst, Span<byte> top, int topOffset) private static void Hu4(Span<byte> dst, ReadOnlySpan<byte> top, int topOffset)
{ {
byte i = top[topOffset - 2]; byte i = top[topOffset - 2];
byte j = top[topOffset - 3]; byte j = top[topOffset - 3];
@ -1088,7 +1088,7 @@ internal static unsafe class Vp8Encoding
private static byte Clip8b(int v) => (v & ~0xff) == 0 ? (byte)v : v < 0 ? (byte)0 : (byte)255; private static byte Clip8b(int v) => (v & ~0xff) == 0 ? (byte)v : v < 0 ? (byte)0 : (byte)255;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Store(Span<byte> dst, Span<byte> reference, int x, int y, int v) => dst[x + (y * WebpConstants.Bps)] = LossyUtils.Clip8B(reference[x + (y * WebpConstants.Bps)] + (v >> 3)); private static void Store(Span<byte> dst, ReadOnlySpan<byte> reference, int x, int y, int v) => dst[x + (y * WebpConstants.Bps)] = LossyUtils.Clip8B(reference[x + (y * WebpConstants.Bps)] + (v >> 3));
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int Mul(int a, int b) => (a * b) >> 16; private static int Mul(int a, int b) => (a * b) >> 16;

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

@ -235,7 +235,7 @@ internal class Vp8Residual
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int LevelCost(Span<ushort> table, int level) private static int LevelCost(ReadOnlySpan<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 static int RecordStats(int bit, Vp8StatsArray statsArr, int idx) private static int RecordStats(int bit, Vp8StatsArray statsArr, int idx)

2
src/ImageSharp/Formats/Webp/Lossy/Vp8SegmentInfo.cs

@ -67,7 +67,7 @@ internal class Vp8SegmentInfo
public int LambdaMode { get; set; } public int LambdaMode { get; set; }
public void StoreMaxDelta(Span<short> dcs) public void StoreMaxDelta(ReadOnlySpan<short> dcs)
{ {
// We look at the first three AC coefficients to determine what is the average // We look at the first three AC coefficients to determine what is the average
// delta between each sub-4x4 block. // delta between each sub-4x4 block.

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

@ -39,7 +39,7 @@ internal static class YuvConversion
} }
} }
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(ReadOnlySpan<byte> topY, ReadOnlySpan<byte> bottomY, ReadOnlySpan<byte> topU, ReadOnlySpan<byte> topV, ReadOnlySpan<byte> curU, ReadOnlySpan<byte> curV, Span<byte> topDst, Span<byte> bottomDst, int len)
{ {
const int xStep = 3; const int xStep = 3;
int lastPixelPair = (len - 1) >> 1; int lastPixelPair = (len - 1) >> 1;
@ -346,7 +346,7 @@ internal static class YuvConversion
/// <param name="y">The destination span for y.</param> /// <param name="y">The destination span for y.</param>
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void ConvertRgbaToY(Span<Bgra32> rowSpan, Span<byte> y, int width) public static void ConvertRgbaToY(ReadOnlySpan<Bgra32> rowSpan, Span<byte> y, int width)
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
@ -361,7 +361,7 @@ internal static class YuvConversion
/// <param name="u">The destination span for u.</param> /// <param name="u">The destination span for u.</param>
/// <param name="v">The destination span for v.</param> /// <param name="v">The destination span for v.</param>
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
public static void ConvertRgbaToUv(Span<ushort> rgb, Span<byte> u, Span<byte> v, int width) public static void ConvertRgbaToUv(ReadOnlySpan<ushort> rgb, Span<byte> u, Span<byte> v, int width)
{ {
int rgbIdx = 0; int rgbIdx = 0;
for (int i = 0; i < width; i += 1, rgbIdx += 4) for (int i = 0; i < width; i += 1, rgbIdx += 4)
@ -372,7 +372,7 @@ internal static class YuvConversion
} }
} }
public static void AccumulateRgb(Span<Bgra32> rowSpan, Span<Bgra32> nextRowSpan, Span<ushort> dst, int width) public static void AccumulateRgb(ReadOnlySpan<Bgra32> rowSpan, ReadOnlySpan<Bgra32> nextRowSpan, Span<ushort> dst, int width)
{ {
Bgra32 bgra0; Bgra32 bgra0;
Bgra32 bgra1; Bgra32 bgra1;
@ -416,7 +416,7 @@ internal static class YuvConversion
} }
} }
public static void AccumulateRgba(Span<Bgra32> rowSpan, Span<Bgra32> nextRowSpan, Span<ushort> dst, int width) public static void AccumulateRgba(ReadOnlySpan<Bgra32> rowSpan, ReadOnlySpan<Bgra32> nextRowSpan, Span<ushort> dst, int width)
{ {
Bgra32 bgra0; Bgra32 bgra0;
Bgra32 bgra1; Bgra32 bgra1;

4
src/ImageSharp/ImageSharp.csproj

@ -30,12 +30,12 @@
<Choose> <Choose>
<When Condition="$(SIXLABORS_TESTING_PREVIEW) == true"> <When Condition="$(SIXLABORS_TESTING_PREVIEW) == true">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks> <TargetFrameworks>net10.0;net11.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
</When> </When>
<Otherwise> <Otherwise>
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks> <TargetFrameworks>net10.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
</Otherwise> </Otherwise>
</Choose> </Choose>

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

@ -412,7 +412,7 @@ public partial class PixelOperations<TPixel>
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
internal static void GuardUnpackIntoRgbPlanes(Span<float> redChannel, Span<float> greenChannel, Span<float> blueChannel, ReadOnlySpan<TPixel> source) internal static void GuardUnpackIntoRgbPlanes(ReadOnlySpan<float> redChannel, Span<float> greenChannel, Span<float> blueChannel, ReadOnlySpan<TPixel> source)
{ {
Guard.IsTrue(greenChannel.Length == redChannel.Length, nameof(greenChannel), "Channels must be of same size!"); Guard.IsTrue(greenChannel.Length == redChannel.Length, nameof(greenChannel), "Channels must be of same size!");
Guard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!"); Guard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!");

2
tests/Directory.Build.targets

@ -23,7 +23,7 @@
Magick.NET 14.15.0 disables WebP-compressed TIFF support on Windows. Magick.NET 14.15.0 disables WebP-compressed TIFF support on Windows.
https://github.com/ImageMagick/tiff/commit/978181b6c999ee013f1b6df0a010d8c550d378cf https://github.com/ImageMagick/tiff/commit/978181b6c999ee013f1b6df0a010d8c550d378cf
--> -->
<PackageReference Update="Magick.NET-Q16-AnyCPU" Version="14.14.0" /> <PackageReference Update="Magick.NET-Q16-AnyCPU" Version="14.16.0" />
<PackageReference Update="Microsoft.DotNet.RemoteExecutor" Version="10.0.0-beta.25563.105" /> <PackageReference Update="Microsoft.DotNet.RemoteExecutor" Version="10.0.0-beta.25563.105" />
<PackageReference Update="Moq" Version="4.20.72" /> <PackageReference Update="Moq" Version="4.20.72" />
<PackageReference Update="NetVips" Version="3.0.0" /> <PackageReference Update="NetVips" Version="3.0.0" />

4
tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj

@ -39,12 +39,12 @@
<Choose> <Choose>
<When Condition="$(SIXLABORS_TESTING_PREVIEW) == true"> <When Condition="$(SIXLABORS_TESTING_PREVIEW) == true">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks> <TargetFrameworks>net10.0;net11.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
</When> </When>
<Otherwise> <Otherwise>
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks> <TargetFrameworks>net10.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
</Otherwise> </Otherwise>
</Choose> </Choose>

4
tests/ImageSharp.PublicApi.Tests/ImageSharp.PublicApi.Tests.csproj

@ -10,12 +10,12 @@
<Choose> <Choose>
<When Condition="$(SIXLABORS_TESTING_PREVIEW) == true"> <When Condition="$(SIXLABORS_TESTING_PREVIEW) == true">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks> <TargetFrameworks>net10.0;net11.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
</When> </When>
<Otherwise> <Otherwise>
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks> <TargetFrameworks>net10.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
</Otherwise> </Otherwise>
</Choose> </Choose>

4
tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj

@ -18,12 +18,12 @@
<Choose> <Choose>
<When Condition="$(SIXLABORS_TESTING_PREVIEW) == true"> <When Condition="$(SIXLABORS_TESTING_PREVIEW) == true">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks> <TargetFrameworks>net10.0;net11.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
</When> </When>
<Otherwise> <Otherwise>
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks> <TargetFrameworks>net10.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
</Otherwise> </Otherwise>
</Choose> </Choose>

10
tests/ImageSharp.Tests/IO/LocalFileSystemTests.cs

@ -50,7 +50,12 @@ public class LocalFileSystemTests
await using (FileStream stream = (FileStream)fs.OpenReadAsynchronous(path)) await using (FileStream stream = (FileStream)fs.OpenReadAsynchronous(path))
using (StreamReader reader = new(stream)) using (StreamReader reader = new(stream))
{ {
// .NET 11 reports IsAsync false for regular files on Unix: https://github.com/dotnet/runtime/pull/125220
#if NET11_0_OR_GREATER
Assert.Equal(OperatingSystem.IsWindows(), stream.IsAsync);
#else
Assert.True(stream.IsAsync); Assert.True(stream.IsAsync);
#endif
Assert.True(stream.CanRead); Assert.True(stream.CanRead);
Assert.False(stream.CanWrite); Assert.False(stream.CanWrite);
@ -105,7 +110,12 @@ public class LocalFileSystemTests
await using (FileStream stream = (FileStream)fs.CreateAsynchronous(path)) await using (FileStream stream = (FileStream)fs.CreateAsynchronous(path))
await using (StreamWriter writer = new(stream)) await using (StreamWriter writer = new(stream))
{ {
// .NET 11 reports IsAsync false for regular files on Unix: https://github.com/dotnet/runtime/pull/125220
#if NET11_0_OR_GREATER
Assert.Equal(OperatingSystem.IsWindows(), stream.IsAsync);
#else
Assert.True(stream.IsAsync); Assert.True(stream.IsAsync);
#endif
Assert.True(stream.CanRead); Assert.True(stream.CanRead);
Assert.True(stream.CanWrite); Assert.True(stream.CanWrite);

4
tests/ImageSharp.Tests/ImageSharp.Tests.csproj

@ -12,12 +12,12 @@
<Choose> <Choose>
<When Condition="$(SIXLABORS_TESTING_PREVIEW) == true"> <When Condition="$(SIXLABORS_TESTING_PREVIEW) == true">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks> <TargetFrameworks>net10.0;net11.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
</When> </When>
<Otherwise> <Otherwise>
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks> <TargetFrameworks>net10.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
</Otherwise> </Otherwise>
</Choose> </Choose>

7
tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs

@ -414,7 +414,14 @@ public static class FeatureTestRunner
// Not a COMPlus value. We filter in calling method. // Not a COMPlus value. We filter in calling method.
features.Add(key, nameof(HwIntrinsics.AllowAll)); features.Add(key, nameof(HwIntrinsics.AllowAll));
break; break;
#if NET11_0_OR_GREATER
case nameof(HwIntrinsics.DisableSSE42):
// SSE3 through SSE4.2 and POPCNT are x86-64-v2 baseline in .NET 11+ and the
// switch is inert, so skip it entirely rather than spawn a child that tests nothing.
// https://learn.microsoft.com/en-us/dotnet/core/compatibility/jit/11/minimum-hardware-requirements
break;
#endif
default: default:
features.Add(key, intrinsic.Replace("Disable", "Enable")); features.Add(key, intrinsic.Replace("Disable", "Enable"));
break; break;

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

@ -11,20 +11,25 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests;
public class FeatureTestRunnerTests public class FeatureTestRunnerTests
{ {
public static TheoryData<HwIntrinsics, string[]> Intrinsics => public static TheoryData<HwIntrinsics, HwIntrinsics, string[]> Intrinsics =>
new() new()
{ {
{ HwIntrinsics.DisableAES | HwIntrinsics.AllowAll, ["EnableAES", "AllowAll"] }, { HwIntrinsics.DisableAES | HwIntrinsics.AllowAll, HwIntrinsics.DisableAES | HwIntrinsics.AllowAll, ["EnableAES", "AllowAll"] },
{ HwIntrinsics.DisableHWIntrinsic, ["EnableHWIntrinsic"] }, { HwIntrinsics.DisableHWIntrinsic, HwIntrinsics.DisableHWIntrinsic, ["EnableHWIntrinsic"] },
{ HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableAVX, ["EnableSSE42", "EnableAVX"] } #if NET11_0_OR_GREATER
// ToFeatureKeyValueCollection filters DisableSSE42: SSE4.2 is x86-64-v2 baseline in .NET 11+ and cannot be disabled.
{ HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableAVX, HwIntrinsics.DisableAVX, ["EnableAVX"] }
#else
{ HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableAVX, HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableAVX, ["EnableSSE42", "EnableAVX"] }
#endif
}; };
[Theory] [Theory]
[MemberData(nameof(Intrinsics))] [MemberData(nameof(Intrinsics))]
public void ToFeatureCollectionReturnsExpectedResult(HwIntrinsics expectedIntrinsics, string[] expectedValues) public void ToFeatureCollectionReturnsExpectedResult(HwIntrinsics intrinsics, HwIntrinsics expectedIntrinsics, string[] expectedValues)
{ {
Dictionary<HwIntrinsics, string> features = expectedIntrinsics.ToFeatureKeyValueCollection(); Dictionary<HwIntrinsics, string> features = intrinsics.ToFeatureKeyValueCollection();
HwIntrinsics[] keys = features.Keys.ToArray(); HwIntrinsics[] keys = [.. features.Keys];
HwIntrinsics actualIntrinsics = keys[0]; HwIntrinsics actualIntrinsics = keys[0];
for (int i = 1; i < keys.Length; i++) for (int i = 1; i < keys.Length; i++)
@ -122,9 +127,8 @@ public class FeatureTestRunnerTests
Assert.False(Ssse3.IsSupported, "Ssse3 should be disabled."); Assert.False(Ssse3.IsSupported, "Ssse3 should be disabled.");
Assert.False(Sse41.IsSupported, "Sse41 should be disabled."); Assert.False(Sse41.IsSupported, "Sse41 should be disabled.");
Assert.False(Popcnt.IsSupported, "Popcnt should be disabled."); Assert.False(Popcnt.IsSupported, "Popcnt should be disabled.");
#else
Assert.False(Sse42.IsSupported, "Sse42 should be disabled when DisableSSE42 is set.");
#endif #endif
Assert.False(Sse42.IsSupported, "Sse42 should be disabled when DisableSSE42 is set.");
break; break;
case HwIntrinsics.DisableAVX: case HwIntrinsics.DisableAVX:
Assert.False(Avx.IsSupported, "AVX should be disabled when DisableAVX is set."); Assert.False(Avx.IsSupported, "AVX should be disabled when DisableAVX is set.");
@ -169,12 +173,12 @@ public class FeatureTestRunnerTests
{ {
Assert.NotNull(serializable); Assert.NotNull(serializable);
Assert.NotNull(FeatureTestRunner.DeserializeForXunit<FakeSerializable>(serializable)); Assert.NotNull(FeatureTestRunner.DeserializeForXunit<FakeSerializable>(serializable));
Assert.False(Sse42.IsSupported, "SSE42 should be disabled when DisableSSE42 is set (sanity check using serializable param overload)."); Assert.False(Avx.IsSupported, "AVX should be disabled when DisableAVX is set (sanity check using serializable param overload).");
} }
FeatureTestRunner.RunWithHwIntrinsicsFeature( FeatureTestRunner.RunWithHwIntrinsicsFeature(
AssertHwIntrinsicsFeatureDisabled, AssertHwIntrinsicsFeatureDisabled,
HwIntrinsics.DisableSSE42, HwIntrinsics.DisableAVX,
new FakeSerializable()); new FakeSerializable());
} }
@ -256,7 +260,7 @@ public class FeatureTestRunnerTests
} }
} }
foreach (HwIntrinsics intrinsic in (HwIntrinsics[])Enum.GetValues(typeof(HwIntrinsics))) foreach (HwIntrinsics intrinsic in Enum.GetValues<HwIntrinsics>())
{ {
FeatureTestRunner.RunWithHwIntrinsicsFeature(AssertHwIntrinsicsFeatureDisabled, intrinsic, new FakeSerializable()); FeatureTestRunner.RunWithHwIntrinsicsFeature(AssertHwIntrinsicsFeatureDisabled, intrinsic, new FakeSerializable());
} }

Loading…
Cancel
Save