diff --git a/.gitattributes b/.gitattributes index 2fdea90e17..ff4ec94087 100644 --- a/.gitattributes +++ b/.gitattributes @@ -64,18 +64,19 @@ # Set explicit file behavior to: # treat as text # normalize to Unix-style line endings and -# use a union merge when resoling conflicts +# use a union merge when resolving conflicts ############################################################################### *.csproj text eol=lf merge=union *.dbproj text eol=lf merge=union *.fsproj text eol=lf merge=union *.ncrunchproject text eol=lf merge=union *.vbproj text eol=lf merge=union +*.shproj text eol=lf merge=union ############################################################################### # Set explicit file behavior to: # treat as text # normalize to Windows-style line endings and -# use a union merge when resoling conflicts +# use a union merge when resolving conflicts ############################################################################### *.sln text eol=crlf merge=union ############################################################################### diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 001244a895..b5cc5daca2 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -56,11 +56,6 @@ jobs: sdk: 6.0.x runtime: -x64 codecov: false - - os: buildjet-4vcpu-ubuntu-2204-arm - framework: net6.0 - sdk: 6.0.x - runtime: -x64 - codecov: false exclude: - isARM: false options: @@ -114,7 +109,6 @@ jobs: if: ${{ matrix.options.sdk-preview != true }} uses: actions/setup-dotnet@v3 with: - include-prerelease: true dotnet-version: | 6.0.x @@ -122,7 +116,6 @@ jobs: if: ${{ matrix.options.sdk-preview == true }} uses: actions/setup-dotnet@v3 with: - include-prerelease: true dotnet-version: | 7.0.x @@ -198,9 +191,16 @@ jobs: shell: pwsh run: ./ci-pack.ps1 - - name: MyGet Publish + - name: Feedz Publish shell: pwsh run: | - dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v2/package - dotnet nuget push .\artifacts\*.snupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v3/index.json - # TODO: If github.ref starts with 'refs/tags' then it was tag push and we can optionally push out package to nuget.org + dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.FEEDZ_TOKEN}} -s https://f.feedz.io/sixlabors/sixlabors/nuget/index.json --skip-duplicate + dotnet nuget push .\artifacts\*.snupkg -k ${{secrets.FEEDZ_TOKEN}} -s https://f.feedz.io/sixlabors/sixlabors/symbols --skip-duplicate + + - name: NuGet Publish + if: ${{ startsWith(github.ref, 'refs/tags/') }} + shell: pwsh + run: | + dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.NUGET_TOKEN}} -s https://api.nuget.org/v3/index.json --skip-duplicate + dotnet nuget push .\artifacts\*.snupkg -k ${{secrets.NUGET_TOKEN}} -s https://api.nuget.org/v3/index.json --skip-duplicate + diff --git a/README.md b/README.md index 4d0eb1e3a6..09262eb572 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,9 @@ For more information, see the [.NET Foundation Code of Conduct](https://dotnetfo Install stable releases via Nuget; development releases are available via MyGet. -| Package Name | Release (NuGet) | Nightly (MyGet) | +| Package Name | Release (NuGet) | Nightly (Feedz.io) | |--------------------------------|-----------------|-----------------| -| `SixLabors.ImageSharp` | [![NuGet](https://img.shields.io/nuget/v/SixLabors.ImageSharp.svg)](https://www.nuget.org/packages/SixLabors.ImageSharp/) | [![MyGet](https://img.shields.io/myget/sixlabors/vpre/SixLabors.ImageSharp.svg)](https://www.myget.org/feed/sixlabors/package/nuget/SixLabors.ImageSharp) | +| `SixLabors.ImageSharp` | [![NuGet](https://img.shields.io/nuget/v/SixLabors.ImageSharp.svg)](https://www.nuget.org/packages/SixLabors.ImageSharp/) | [![feedz.io](https://img.shields.io/badge/endpoint.svg?url=https%3A%2F%2Ff.feedz.io%2Fsixlabors%2Fsixlabors%2Fshield%2FSixLabors.ImageSharp%2Flatest)](https://f.feedz.io/sixlabors/sixlabors/nuget/index.json) | ## Manual build @@ -64,7 +64,7 @@ If you prefer, you can compile ImageSharp yourself (please do and help!) - Using [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) - Make sure you have the latest version installed - - Make sure you have [the .NET 6 SDK](https://www.microsoft.com/net/core#windows) installed + - Make sure you have [the .NET 7 SDK](https://www.microsoft.com/net/core#windows) installed Alternatively, you can work from command line and/or with a lightweight editor on **both Linux/Unix and Windows**: diff --git a/ci-pack.ps1 b/ci-pack.ps1 index 09f45347ef..55c69fb590 100644 --- a/ci-pack.ps1 +++ b/ci-pack.ps1 @@ -3,4 +3,4 @@ dotnet clean -c Release $repositoryUrl = "https://github.com/$env:GITHUB_REPOSITORY" # Building for packing and publishing. -dotnet pack -c Release --output "$PSScriptRoot/artifacts" /p:RepositoryUrl=$repositoryUrl +dotnet pack -c Release -p:PackageOutputPath="$PSScriptRoot/artifacts" -p:RepositoryUrl=$repositoryUrl diff --git a/shared-infrastructure b/shared-infrastructure index 6c52486c51..9a6cf00d9a 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 6c52486c512357475cbb92bbb7c4c0af4d85b1db +Subproject commit 9a6cf00d9a3d482bb08211dd8309f4724a2735cb diff --git a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs index e776a0dc20..4c3923c888 100644 --- a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs @@ -167,7 +167,7 @@ public static class SRgbCompanding [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe void CompandAvx2(Span vectors, float[] table) { - fixed (float* tablePointer = &table[0]) + fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) { var scale = Vector256.Create((float)Scale); Vector256 zero = Vector256.Zero; @@ -175,7 +175,7 @@ public static class SRgbCompanding // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 ref Vector256 vectorsBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors)); - ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (IntPtr)((uint)vectors.Length / 2u)); + ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length / 2u); while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) { @@ -199,12 +199,12 @@ public static class SRgbCompanding [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe void CompandScalar(Span vectors, float[] table) { - fixed (float* tablePointer = &table[0]) + fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) { Vector4 zero = Vector4.Zero; var scale = new Vector4(Scale); ref Vector4 vectorsBase = ref MemoryMarshal.GetReference(vectors); - ref Vector4 vectorsLast = ref Unsafe.Add(ref vectorsBase, vectors.Length); + ref Vector4 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length); while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) { diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs index 721df36678..54667ca2af 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs @@ -37,7 +37,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); @@ -70,7 +70,7 @@ public partial class ColorSpaceConverter ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); @@ -103,7 +103,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); @@ -136,7 +136,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); @@ -169,7 +169,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); @@ -201,7 +201,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); @@ -234,7 +234,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); @@ -266,7 +266,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); @@ -299,7 +299,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); @@ -332,7 +332,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); @@ -365,7 +365,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); @@ -398,7 +398,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); @@ -431,7 +431,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref CieLab dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs index da8e68b480..9949b5d91b 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs @@ -36,7 +36,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); @@ -69,7 +69,7 @@ public partial class ColorSpaceConverter ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); @@ -102,7 +102,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); @@ -135,7 +135,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); @@ -168,7 +168,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); @@ -200,7 +200,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); @@ -233,7 +233,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); @@ -266,7 +266,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); @@ -299,7 +299,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); @@ -332,7 +332,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); @@ -365,7 +365,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); @@ -398,7 +398,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); @@ -431,7 +431,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref CieLch dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs index 75e955e41f..4b856d1189 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs @@ -36,7 +36,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); @@ -69,7 +69,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); @@ -102,7 +102,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); @@ -135,7 +135,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); @@ -168,7 +168,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); @@ -201,7 +201,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); @@ -234,7 +234,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); @@ -267,7 +267,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); @@ -300,7 +300,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); @@ -333,7 +333,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); @@ -366,7 +366,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); @@ -399,7 +399,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); @@ -431,7 +431,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs index b04acc9907..2e8029f64a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs @@ -35,7 +35,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); @@ -67,7 +67,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); @@ -102,7 +102,7 @@ public partial class ColorSpaceConverter ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); @@ -134,7 +134,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); @@ -169,7 +169,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); @@ -201,7 +201,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); @@ -233,7 +233,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); @@ -265,7 +265,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); @@ -297,7 +297,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); @@ -329,7 +329,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); @@ -361,7 +361,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); @@ -393,7 +393,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); @@ -425,7 +425,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref CieLuv dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs index a3851de9f0..13b2a225c3 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs @@ -36,7 +36,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); @@ -69,7 +69,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); @@ -102,7 +102,7 @@ public partial class ColorSpaceConverter ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); @@ -135,7 +135,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); @@ -163,7 +163,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); @@ -196,7 +196,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); @@ -229,7 +229,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); @@ -262,7 +262,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); @@ -295,7 +295,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); @@ -328,7 +328,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); @@ -361,7 +361,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); @@ -394,7 +394,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); @@ -427,7 +427,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyy dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs index 1244655227..2212ca2e58 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs @@ -41,7 +41,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); @@ -76,7 +76,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); @@ -111,7 +111,7 @@ public partial class ColorSpaceConverter ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); @@ -146,7 +146,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); @@ -177,7 +177,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); @@ -210,7 +210,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); @@ -243,7 +243,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); @@ -277,7 +277,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); @@ -310,7 +310,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); @@ -345,7 +345,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); @@ -374,7 +374,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); @@ -407,7 +407,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); @@ -440,7 +440,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs index cadcc9e03f..ea9a5d734b 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs @@ -36,7 +36,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); @@ -69,7 +69,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); @@ -102,7 +102,7 @@ public partial class ColorSpaceConverter ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); @@ -135,7 +135,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); @@ -168,7 +168,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); @@ -201,7 +201,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); @@ -234,7 +234,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); @@ -267,7 +267,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); @@ -300,7 +300,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); @@ -333,7 +333,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); @@ -366,7 +366,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); @@ -394,7 +394,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); @@ -427,7 +427,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs index b763a3ebe7..67ec162917 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs @@ -36,7 +36,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); @@ -69,7 +69,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); @@ -102,7 +102,7 @@ public partial class ColorSpaceConverter ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); @@ -135,7 +135,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); @@ -168,7 +168,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); @@ -201,7 +201,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); @@ -234,7 +234,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); @@ -267,7 +267,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); @@ -300,7 +300,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); @@ -333,7 +333,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); @@ -366,7 +366,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); @@ -394,7 +394,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); @@ -427,7 +427,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs index 4b4b9d0077..47ee42dc5a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs @@ -36,7 +36,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); @@ -69,7 +69,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); @@ -102,7 +102,7 @@ public partial class ColorSpaceConverter ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); @@ -135,7 +135,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); @@ -168,7 +168,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); @@ -201,7 +201,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); @@ -234,7 +234,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); @@ -267,7 +267,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); @@ -300,7 +300,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); @@ -333,7 +333,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); @@ -366,7 +366,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); @@ -394,7 +394,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); @@ -427,7 +427,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs index 01c040231a..0604027760 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs @@ -24,7 +24,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); @@ -45,7 +45,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); @@ -66,7 +66,7 @@ public partial class ColorSpaceConverter ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); @@ -87,7 +87,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); @@ -108,7 +108,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); @@ -129,7 +129,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); @@ -150,7 +150,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); @@ -171,7 +171,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); @@ -192,7 +192,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); @@ -213,7 +213,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); @@ -234,7 +234,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); @@ -255,7 +255,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); @@ -276,7 +276,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref HunterLab dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs index 416274e003..fd385a15b0 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs @@ -24,7 +24,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); @@ -45,7 +45,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); @@ -66,7 +66,7 @@ public partial class ColorSpaceConverter ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); @@ -87,7 +87,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); @@ -108,7 +108,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); @@ -129,7 +129,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); @@ -150,7 +150,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); @@ -171,7 +171,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); @@ -192,7 +192,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); @@ -213,7 +213,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); @@ -234,7 +234,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); @@ -255,7 +255,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); @@ -276,7 +276,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs index e2870a6eb4..56f61ef80b 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Runtime.CompilerServices; @@ -24,7 +24,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); @@ -45,7 +45,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); @@ -66,7 +66,7 @@ public partial class ColorSpaceConverter ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); @@ -87,7 +87,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); @@ -108,7 +108,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); @@ -129,7 +129,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); @@ -150,7 +150,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); @@ -171,7 +171,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); @@ -192,7 +192,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); @@ -213,7 +213,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); @@ -234,7 +234,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); @@ -255,7 +255,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); @@ -276,7 +276,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref Lms destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref Lms dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs index 7346a28f33..080e1fc4bf 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs @@ -24,7 +24,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); @@ -45,7 +45,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); @@ -66,7 +66,7 @@ public partial class ColorSpaceConverter ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); @@ -87,7 +87,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); @@ -108,7 +108,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); @@ -129,7 +129,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); @@ -150,7 +150,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); @@ -171,7 +171,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); @@ -192,7 +192,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); @@ -213,7 +213,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); @@ -234,7 +234,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); @@ -255,7 +255,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); @@ -276,7 +276,7 @@ public partial class ColorSpaceConverter ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs index f267a0d89d..da8e046ff7 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs @@ -24,7 +24,7 @@ public partial class ColorSpaceConverter ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr dp = ref Unsafe.Add(ref destRef, i); @@ -45,7 +45,7 @@ public partial class ColorSpaceConverter ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr dp = ref Unsafe.Add(ref destRef, i); @@ -66,7 +66,7 @@ public partial class ColorSpaceConverter ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr dp = ref Unsafe.Add(ref destRef, i); @@ -87,7 +87,7 @@ public partial class ColorSpaceConverter ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr dp = ref Unsafe.Add(ref destRef, i); @@ -108,7 +108,7 @@ public partial class ColorSpaceConverter ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr dp = ref Unsafe.Add(ref destRef, i); @@ -129,7 +129,7 @@ public partial class ColorSpaceConverter ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr dp = ref Unsafe.Add(ref destRef, i); @@ -150,7 +150,7 @@ public partial class ColorSpaceConverter ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr dp = ref Unsafe.Add(ref destRef, i); @@ -171,7 +171,7 @@ public partial class ColorSpaceConverter ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr dp = ref Unsafe.Add(ref destRef, i); @@ -192,7 +192,7 @@ public partial class ColorSpaceConverter ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr dp = ref Unsafe.Add(ref destRef, i); @@ -213,7 +213,7 @@ public partial class ColorSpaceConverter ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr dp = ref Unsafe.Add(ref destRef, i); @@ -234,7 +234,7 @@ public partial class ColorSpaceConverter ref Lms sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Lms sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr dp = ref Unsafe.Add(ref destRef, i); @@ -255,7 +255,7 @@ public partial class ColorSpaceConverter ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref YCbCr dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs index df7686c316..0ce6e3c9ff 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Runtime.CompilerServices; @@ -42,9 +42,11 @@ internal sealed class CieXyzToCieLabConverter float xr = input.X / wx, yr = input.Y / wy, zr = input.Z / wz; - float fx = xr > CieConstants.Epsilon ? MathF.Pow(xr, 0.3333333F) : ((CieConstants.Kappa * xr) + 16F) / 116F; - float fy = yr > CieConstants.Epsilon ? MathF.Pow(yr, 0.3333333F) : ((CieConstants.Kappa * yr) + 16F) / 116F; - float fz = zr > CieConstants.Epsilon ? MathF.Pow(zr, 0.3333333F) : ((CieConstants.Kappa * zr) + 16F) / 116F; + const float inv116 = 1 / 116F; + + float fx = xr > CieConstants.Epsilon ? MathF.Pow(xr, 0.3333333F) : ((CieConstants.Kappa * xr) + 16F) * inv116; + float fy = yr > CieConstants.Epsilon ? MathF.Pow(yr, 0.3333333F) : ((CieConstants.Kappa * yr) + 16F) * inv116; + float fz = zr > CieConstants.Epsilon ? MathF.Pow(zr, 0.3333333F) : ((CieConstants.Kappa * zr) + 16F) * inv116; float l = (116F * fy) - 16F; float a = 500F * (fx - fy); diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs index 7b9915c23f..97e9cee813 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs @@ -81,7 +81,7 @@ public sealed class VonKriesChromaticAdaptation : IChromaticAdaptation ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); ref CieXyz dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/Common/Constants.cs b/src/ImageSharp/Common/Constants.cs index fa2f72c74a..d4640f133f 100644 --- a/src/ImageSharp/Common/Constants.cs +++ b/src/ImageSharp/Common/Constants.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp; diff --git a/src/ImageSharp/Common/Helpers/ColorNumerics.cs b/src/ImageSharp/Common/Helpers/ColorNumerics.cs index 47c4b296bd..553a7c2e89 100644 --- a/src/ImageSharp/Common/Helpers/ColorNumerics.cs +++ b/src/ImageSharp/Common/Helpers/ColorNumerics.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace SixLabors.ImageSharp; @@ -17,7 +16,7 @@ internal static class ColorNumerics /// Vector for converting pixel to gray value as specified by /// ITU-R Recommendation BT.709. /// - private static readonly Vector4 Bt709 = new Vector4(.2126f, .7152f, .0722f, 0.0f); + private static readonly Vector4 Bt709 = new(.2126f, .7152f, .0722f, 0.0f); /// /// Convert a pixel value to grayscale using ITU-R Recommendation BT.709. @@ -137,6 +136,19 @@ internal static class ColorNumerics public static int GetColorCountForBitDepth(int bitDepth) => 1 << bitDepth; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector4 Transform(Vector4 vector, in ColorMatrix.Impl matrix) + { + Vector4 result = matrix.X * vector.X; + + result += matrix.Y * vector.Y; + result += matrix.Z * vector.Z; + result += matrix.W * vector.W; + result += matrix.V; + + return result; + } + /// /// Transforms a vector by the given color matrix. /// @@ -144,17 +156,7 @@ internal static class ColorNumerics /// The transformation color matrix. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Transform(ref Vector4 vector, ref ColorMatrix matrix) - { - float x = vector.X; - float y = vector.Y; - float z = vector.Z; - float w = vector.W; - - vector.X = (x * matrix.M11) + (y * matrix.M21) + (z * matrix.M31) + (w * matrix.M41) + matrix.M51; - vector.Y = (x * matrix.M12) + (y * matrix.M22) + (z * matrix.M32) + (w * matrix.M42) + matrix.M52; - vector.Z = (x * matrix.M13) + (y * matrix.M23) + (z * matrix.M33) + (w * matrix.M43) + matrix.M53; - vector.W = (x * matrix.M14) + (y * matrix.M24) + (z * matrix.M34) + (w * matrix.M44) + matrix.M54; - } + => vector = Transform(vector, matrix.AsImpl()); /// /// Bulk variant of . @@ -164,11 +166,9 @@ internal static class ColorNumerics [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Transform(Span vectors, ref ColorMatrix matrix) { - ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); - for (int i = 0; i < vectors.Length; i++) { - ref Vector4 v = ref Unsafe.Add(ref baseRef, i); + ref Vector4 v = ref vectors[i]; Transform(ref v, ref matrix); } } diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index 95211ccda8..96cd094cd8 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Runtime.CompilerServices; using SixLabors.ImageSharp; @@ -17,13 +16,14 @@ internal static partial class Guard /// The type of the value. /// is not a value type. [MethodImpl(InliningOptions.ShortMethod)] - public static void MustBeValueType(TValue value, string parameterName) + public static void MustBeValueType(TValue value, [CallerArgumentExpression("value")] string? parameterName = null) + where TValue : notnull { if (value.GetType().IsValueType) { return; } - ThrowHelper.ThrowArgumentException("Type must be a struct.", parameterName); + ThrowHelper.ThrowArgumentException("Type must be a struct.", parameterName!); } } diff --git a/src/ImageSharp/Common/Helpers/HexConverter.cs b/src/ImageSharp/Common/Helpers/HexConverter.cs index 7ec0ca625c..8c473688f3 100644 --- a/src/ImageSharp/Common/Helpers/HexConverter.cs +++ b/src/ImageSharp/Common/Helpers/HexConverter.cs @@ -16,21 +16,19 @@ internal static class HexConverter /// The number of bytes written to . public static int HexStringToBytes(ReadOnlySpan chars, Span bytes) { - if ((chars.Length % 2) != 0) + if (Numerics.Modulo2(chars.Length) != 0) { throw new ArgumentException("Input string length must be a multiple of 2", nameof(chars)); } - if ((bytes.Length * 2) < chars.Length) + if ((bytes.Length << 1 /* bit-hack for *2 */) < chars.Length) { throw new ArgumentException("Output span must be at least half the length of the input string"); } - else - { - // Slightly better performance in the loop below, allows us to skip a bounds check - // while still supporting output buffers that are larger than necessary - bytes = bytes[..(chars.Length / 2)]; - } + + // Slightly better performance in the loop below, allows us to skip a bounds check + // while still supporting output buffers that are larger than necessary + bytes = bytes[..(chars.Length >> 1)]; // bit-hack for / 2 [MethodImpl(MethodImplOptions.AggressiveInlining)] static int FromChar(int c) @@ -57,7 +55,7 @@ internal static class HexConverter 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 255 }; - return c >= charToHexLookup.Length ? 0xFF : charToHexLookup[c]; + return (uint)c >= (uint)charToHexLookup.Length ? 0xFF : charToHexLookup[c]; } // See https://source.dot.net/#System.Private.CoreLib/HexConverter.cs,4681d45a0aa0b361 diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index fc6cfd585a..5af5db3cda 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -5,6 +5,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp; @@ -54,6 +55,12 @@ internal static class Numerics [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Modulo4(int x) => x & 3; + /// + /// Calculates % 4 + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nint Modulo4(nint x) => x & 3; + /// /// Calculates % 8 /// @@ -290,7 +297,7 @@ internal static class Numerics if (remainder.Length > 0) { ref byte remainderStart = ref MemoryMarshal.GetReference(remainder); - ref byte remainderEnd = ref Unsafe.Add(ref remainderStart, remainder.Length); + ref byte remainderEnd = ref Unsafe.Add(ref remainderStart, (uint)remainder.Length); while (Unsafe.IsAddressLessThan(ref remainderStart, ref remainderEnd)) { @@ -315,7 +322,7 @@ internal static class Numerics if (remainder.Length > 0) { ref uint remainderStart = ref MemoryMarshal.GetReference(remainder); - ref uint remainderEnd = ref Unsafe.Add(ref remainderStart, remainder.Length); + ref uint remainderEnd = ref Unsafe.Add(ref remainderStart, (uint)remainder.Length); while (Unsafe.IsAddressLessThan(ref remainderStart, ref remainderEnd)) { @@ -340,7 +347,7 @@ internal static class Numerics if (remainder.Length > 0) { ref int remainderStart = ref MemoryMarshal.GetReference(remainder); - ref int remainderEnd = ref Unsafe.Add(ref remainderStart, remainder.Length); + ref int remainderEnd = ref Unsafe.Add(ref remainderStart, (uint)remainder.Length); while (Unsafe.IsAddressLessThan(ref remainderStart, ref remainderEnd)) { @@ -365,7 +372,7 @@ internal static class Numerics if (remainder.Length > 0) { ref float remainderStart = ref MemoryMarshal.GetReference(remainder); - ref float remainderEnd = ref Unsafe.Add(ref remainderStart, remainder.Length); + ref float remainderEnd = ref Unsafe.Add(ref remainderStart, (uint)remainder.Length); while (Unsafe.IsAddressLessThan(ref remainderStart, ref remainderEnd)) { @@ -390,7 +397,7 @@ internal static class Numerics if (remainder.Length > 0) { ref double remainderStart = ref MemoryMarshal.GetReference(remainder); - ref double remainderEnd = ref Unsafe.Add(ref remainderStart, remainder.Length); + ref double remainderEnd = ref Unsafe.Add(ref remainderStart, (uint)remainder.Length); while (Unsafe.IsAddressLessThan(ref remainderStart, ref remainderEnd)) { @@ -429,9 +436,9 @@ internal static class Numerics var vmin = new Vector(min); var vmax = new Vector(max); - int n = span.Length / Vector.Count; - int m = Modulo4(n); - int u = n - m; + nint n = (nint)(uint)span.Length / Vector.Count; + nint m = Modulo4(n); + nint u = n - m; ref Vector vs0 = ref Unsafe.As>(ref MemoryMarshal.GetReference(span)); ref Vector vs1 = ref Unsafe.Add(ref vs0, 1); @@ -473,21 +480,10 @@ internal static class Numerics [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Premultiply(ref Vector4 source) { - float w = source.W; - source *= w; - source.W = w; - } - - /// - /// Reverses the result of premultiplying a vector via . - /// - /// The to premultiply - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void UnPremultiply(ref Vector4 source) - { - float w = source.W; - source /= w; - source.W = w; + // Load into a local variable to prevent accessing the source from memory multiple times. + Vector4 src = source; + Vector4 alpha = PermuteW(src); + source = WithW(src * alpha, alpha); } /// @@ -497,17 +493,17 @@ internal static class Numerics [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Premultiply(Span vectors) { - if (Avx2.IsSupported && vectors.Length >= 2) + if (Avx.IsSupported && vectors.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 ref Vector256 vectorsBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors)); - ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (IntPtr)((uint)vectors.Length / 2u)); + ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length / 2u); while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) { Vector256 source = vectorsBase; - Vector256 multiply = Avx.Shuffle(source, source, ShuffleAlphaControl); - vectorsBase = Avx.Blend(Avx.Multiply(source, multiply), source, BlendAlphaControl); + Vector256 alpha = Avx.Permute(source, ShuffleAlphaControl); + vectorsBase = Avx.Blend(Avx.Multiply(source, alpha), source, BlendAlphaControl); vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); } @@ -520,7 +516,7 @@ internal static class Numerics else { ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors); - ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, vectors.Length); + ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, (uint)vectors.Length); while (Unsafe.IsAddressLessThan(ref vectorsStart, ref vectorsEnd)) { @@ -531,6 +527,30 @@ internal static class Numerics } } + /// + /// Reverses the result of premultiplying a vector via . + /// + /// The to premultiply + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void UnPremultiply(ref Vector4 source) + { + Vector4 alpha = PermuteW(source); + UnPremultiply(ref source, alpha); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void UnPremultiply(ref Vector4 source, Vector4 alpha) + { + if (alpha == Vector4.Zero) + { + return; + } + + // Divide source by alpha if alpha is nonzero, otherwise set all components to match the source value + // Blend the result with the alpha vector to ensure that the alpha component is unchanged + source = WithW(source / alpha, alpha); + } + /// /// Bulk variant of /// @@ -538,17 +558,18 @@ internal static class Numerics [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UnPremultiply(Span vectors) { - if (Avx2.IsSupported && vectors.Length >= 2) + if (Avx.IsSupported && vectors.Length >= 2) { // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 ref Vector256 vectorsBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors)); - ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (IntPtr)((uint)vectors.Length / 2u)); + ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length / 2u); + Vector256 epsilon = Vector256.Create(Constants.Epsilon); while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) { Vector256 source = vectorsBase; - Vector256 multiply = Avx.Shuffle(source, source, ShuffleAlphaControl); - vectorsBase = Avx.Blend(Avx.Divide(source, multiply), source, BlendAlphaControl); + Vector256 alpha = Avx.Permute(source, ShuffleAlphaControl); + vectorsBase = UnPremultiply(source, alpha); vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); } @@ -561,7 +582,7 @@ internal static class Numerics else { ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors); - ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, vectors.Length); + ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, (uint)vectors.Length); while (Unsafe.IsAddressLessThan(ref vectorsStart, ref vectorsEnd)) { @@ -572,6 +593,61 @@ internal static class Numerics } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 UnPremultiply(Vector256 source, Vector256 alpha) + { + // Check if alpha is zero to avoid division by zero + Vector256 zeroMask = Avx.CompareEqual(alpha, Vector256.Zero); + + // Divide source by alpha if alpha is nonzero, otherwise set all components to match the source value + Vector256 result = Avx.BlendVariable(Avx.Divide(source, alpha), source, zeroMask); + + // Blend the result with the alpha vector to ensure that the alpha component is unchanged + return Avx.Blend(result, alpha, BlendAlphaControl); + } + + /// + /// Permutes the given vector return a new instance with all the values set to . + /// + /// The vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 PermuteW(Vector4 value) + { + if (Sse.IsSupported) + { + return Sse.Shuffle(value.AsVector128(), value.AsVector128(), ShuffleAlphaControl).AsVector4(); + } + + return new(value.W); + } + + /// + /// Sets the W component of the given vector to the given value from . + /// + /// The vector to set. + /// The vector containing the W value. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 WithW(Vector4 value, Vector4 w) + { + if (Sse41.IsSupported) + { + return Sse41.Insert(value.AsVector128(), w.AsVector128(), 0b11_11_0000).AsVector4(); + } + + if (Sse.IsSupported) + { + // Create tmp as + // Then return (which is ) + Vector128 tmp = Sse.Shuffle(w.AsVector128(), value.AsVector128(), 0b00_10_00_11); + return Sse.Shuffle(value.AsVector128(), tmp, 0b00_10_01_00).AsVector4(); + } + + value.W = w.W; + return value; + } + /// /// Calculates the cube pow of all the XYZ channels of the input vectors. /// @@ -580,12 +656,12 @@ internal static class Numerics public static unsafe void CubePowOnXYZ(Span vectors) { ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); - ref Vector4 endRef = ref Unsafe.Add(ref baseRef, vectors.Length); + ref Vector4 endRef = ref Unsafe.Add(ref baseRef, (uint)vectors.Length); while (Unsafe.IsAddressLessThan(ref baseRef, ref endRef)) { Vector4 v = baseRef; - float a = v.W; + Vector4 a = PermuteW(v); // Fast path for the default gamma exposure, which is 3. In this case we can skip // calling Math.Pow 3 times (one per component), as the method is an internal call and @@ -594,7 +670,7 @@ internal static class Numerics // back to the target index in the temporary span. The whole iteration will get completely // inlined and traslated into vectorized instructions, with much better performance. v = v * v * v; - v.W = a; + v = WithW(v, a); baseRef = v; baseRef = ref Unsafe.Add(ref baseRef, 1); @@ -611,7 +687,7 @@ internal static class Numerics if (Sse41.IsSupported) { ref Vector128 vectors128Ref = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors)); - ref Vector128 vectors128End = ref Unsafe.Add(ref vectors128Ref, vectors.Length); + ref Vector128 vectors128End = ref Unsafe.Add(ref vectors128Ref, (uint)vectors.Length); var v128_341 = Vector128.Create(341); Vector128 v128_negativeZero = Vector128.Create(-0.0f).AsInt32(); @@ -660,7 +736,7 @@ internal static class Numerics else { ref Vector4 vectorsRef = ref MemoryMarshal.GetReference(vectors); - ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsRef, vectors.Length); + ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsRef, (uint)vectors.Length); // Fallback with scalar preprocessing and vectorized approximation steps while (Unsafe.IsAddressLessThan(ref vectorsRef, ref vectorsEnd)) @@ -808,6 +884,25 @@ internal static class Numerics return Sse2.ConvertToInt32(vsum); } + /// + /// Reduces elements of the vector into one sum. + /// + /// The accumulator to reduce. + /// The sum of all elements. + [MethodImpl(InliningOptions.ShortMethod)] + public static int ReduceSumArm(Vector128 accumulator) + { + if (AdvSimd.Arm64.IsSupported) + { + Vector64 sum = AdvSimd.Arm64.AddAcross(accumulator); + return (int)AdvSimd.Extract(sum, 0); + } + + Vector128 sum2 = AdvSimd.AddPairwiseWidening(accumulator); + Vector64 sum3 = AdvSimd.Add(sum2.GetLower().AsUInt32(), sum2.GetUpper().AsUInt32()); + return (int)AdvSimd.Extract(sum3, 0); + } + /// /// Reduces even elements of the vector into one sum. /// @@ -854,4 +949,94 @@ internal static class Numerics [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsOutOfRange(int value, int min, int max) => (uint)(value - min) > (uint)(max - min); + + /// + /// Gets the count of vectors that safely fit into the given span. + /// + /// The type of the vector. + /// The given span. + /// Count of vectors that safely fit into the span. + public static nuint VectorCount(this Span span) + where TVector : struct + => (uint)span.Length / (uint)Vector.Count; + + /// + /// Gets the count of vectors that safely fit into the given span. + /// + /// The type of the vector. + /// The given span. + /// Count of vectors that safely fit into the span. + public static nuint Vector128Count(this Span span) + where TVector : struct + => (uint)span.Length / (uint)Vector128.Count; + + /// + /// Gets the count of vectors that safely fit into the given span. + /// + /// The type of the vector. + /// The given span. + /// Count of vectors that safely fit into the span. + public static nuint Vector128Count(this ReadOnlySpan span) + where TVector : struct + => (uint)span.Length / (uint)Vector128.Count; + + /// + /// Gets the count of vectors that safely fit into the given span. + /// + /// The type of the vector. + /// The given span. + /// Count of vectors that safely fit into the span. + public static nuint Vector256Count(this Span span) + where TVector : struct + => (uint)span.Length / (uint)Vector256.Count; + + /// + /// Gets the count of vectors that safely fit into the given span. + /// + /// The type of the vector. + /// The given span. + /// Count of vectors that safely fit into the span. + public static nuint Vector256Count(this ReadOnlySpan span) + where TVector : struct + => (uint)span.Length / (uint)Vector256.Count; + + /// + /// Gets the count of vectors that safely fit into the given span. + /// + /// The type of the vector. + /// The given span. + /// Count of vectors that safely fit into the span. + public static nuint VectorCount(this Span span) + where TVector : struct + => (uint)span.Length / (uint)Vector.Count; + + /// + /// Gets the count of vectors that safely fit into the given span. + /// + /// The type of the vector. + /// The given span. + /// Count of vectors that safely fit into the span. + public static nuint Vector128Count(this Span span) + where TVector : struct + => (uint)span.Length / (uint)Vector128.Count; + + /// + /// Gets the count of vectors that safely fit into the given span. + /// + /// The type of the vector. + /// The given span. + /// Count of vectors that safely fit into the span. + public static nuint Vector256Count(this Span span) + where TVector : struct + => (uint)span.Length / (uint)Vector256.Count; + + /// + /// Gets the count of vectors that safely fit into length. + /// + /// The type of the vector. + /// The given length. + /// Count of vectors that safely fit into the length. + public static nuint Vector256Count(int length) + where TVector : struct + => (uint)length / (uint)Vector256.Count; } diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs index 45d6e6d13d..683ac518b8 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs @@ -5,6 +5,7 @@ using System.Buffers.Binary; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using static SixLabors.ImageSharp.SimdUtils; // The JIT can detect and optimize rotation idioms ROTL (Rotate Left) // and ROTR (Rotate Right) emitting efficient CPU instructions: @@ -18,9 +19,12 @@ namespace SixLabors.ImageSharp; internal interface IComponentShuffle { /// - /// Gets the shuffle control. + /// Shuffles then slices 8-bit integers within 128-bit lanes in + /// using the control and store the results in . /// - byte Control { get; } + /// The source span of bytes. + /// The destination span of bytes. + void ShuffleReduce(ref ReadOnlySpan source, ref Span dest); /// /// Shuffle 8-bit integers within 128-bit lanes in @@ -42,41 +46,26 @@ internal interface IShuffle4 : IComponentShuffle internal readonly struct DefaultShuffle4 : IShuffle4 { - private readonly byte p3; - private readonly byte p2; - private readonly byte p1; - private readonly byte p0; - - public DefaultShuffle4(byte p3, byte p2, byte p1, byte p0) - { - DebugGuard.MustBeBetweenOrEqualTo(p3, 0, 3, nameof(p3)); - DebugGuard.MustBeBetweenOrEqualTo(p2, 0, 3, nameof(p2)); - DebugGuard.MustBeBetweenOrEqualTo(p1, 0, 3, nameof(p1)); - DebugGuard.MustBeBetweenOrEqualTo(p0, 0, 3, nameof(p0)); - - this.p3 = p3; - this.p2 = p2; - this.p1 = p1; - this.p0 = p0; - this.Control = SimdUtils.Shuffle.MmShuffle(p3, p2, p1, p0); - } + public DefaultShuffle4(byte control) + => this.Control = control; public byte Control { get; } + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) + => HwIntrinsics.Shuffle4Reduce(ref source, ref dest, this.Control); + [MethodImpl(InliningOptions.ShortMethod)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte dBase = ref MemoryMarshal.GetReference(dest); - int p3 = this.p3; - int p2 = this.p2; - int p1 = this.p1; - int p0 = this.p0; + Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); - for (int i = 0; i < source.Length; i += 4) + for (nuint i = 0; i < (uint)source.Length; i += 4) { - Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, p0 + i); + Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + i); Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i); Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i); Unsafe.Add(ref dBase, i + 3) = Unsafe.Add(ref sBase, p3 + i); @@ -86,20 +75,18 @@ internal readonly struct DefaultShuffle4 : IShuffle4 internal readonly struct WXYZShuffle4 : IShuffle4 { - public byte Control - { - [MethodImpl(InliningOptions.ShortMethod)] - get => SimdUtils.Shuffle.MmShuffle(2, 1, 0, 3); - } + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) + => HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle2103); [MethodImpl(InliningOptions.ShortMethod)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); - int n = source.Length / 4; + uint n = (uint)source.Length / 4; - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { uint packed = Unsafe.Add(ref sBase, i); @@ -112,20 +99,18 @@ internal readonly struct WXYZShuffle4 : IShuffle4 internal readonly struct WZYXShuffle4 : IShuffle4 { - public byte Control - { - [MethodImpl(InliningOptions.ShortMethod)] - get => SimdUtils.Shuffle.MmShuffle(0, 1, 2, 3); - } + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) + => HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle0123); [MethodImpl(InliningOptions.ShortMethod)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); - int n = source.Length / 4; + uint n = (uint)source.Length / 4; - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { uint packed = Unsafe.Add(ref sBase, i); @@ -138,20 +123,18 @@ internal readonly struct WZYXShuffle4 : IShuffle4 internal readonly struct YZWXShuffle4 : IShuffle4 { - public byte Control - { - [MethodImpl(InliningOptions.ShortMethod)] - get => SimdUtils.Shuffle.MmShuffle(0, 3, 2, 1); - } + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) + => HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle0321); [MethodImpl(InliningOptions.ShortMethod)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); - int n = source.Length / 4; + uint n = (uint)source.Length / 4; - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { uint packed = Unsafe.Add(ref sBase, i); @@ -164,20 +147,18 @@ internal readonly struct YZWXShuffle4 : IShuffle4 internal readonly struct ZYXWShuffle4 : IShuffle4 { - public byte Control - { - [MethodImpl(InliningOptions.ShortMethod)] - get => SimdUtils.Shuffle.MmShuffle(3, 0, 1, 2); - } + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) + => HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle3012); [MethodImpl(InliningOptions.ShortMethod)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); - int n = source.Length / 4; + uint n = (uint)source.Length / 4; - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { uint packed = Unsafe.Add(ref sBase, i); @@ -197,20 +178,18 @@ internal readonly struct ZYXWShuffle4 : IShuffle4 internal readonly struct XWZYShuffle4 : IShuffle4 { - public byte Control - { - [MethodImpl(InliningOptions.ShortMethod)] - get => SimdUtils.Shuffle.MmShuffle(1, 2, 3, 0); - } + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) + => HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle1230); [MethodImpl(InliningOptions.ShortMethod)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); - int n = source.Length / 4; + uint n = (uint)source.Length / 4; - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { uint packed = Unsafe.Add(ref sBase, i); diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs index 76cffd82bf..6cf6eef08e 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using static SixLabors.ImageSharp.SimdUtils; namespace SixLabors.ImageSharp; @@ -13,48 +14,33 @@ internal interface IPad3Shuffle4 : IComponentShuffle internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4 { - private readonly byte p3; - private readonly byte p2; - private readonly byte p1; - private readonly byte p0; - - public DefaultPad3Shuffle4(byte p3, byte p2, byte p1, byte p0) - { - DebugGuard.MustBeBetweenOrEqualTo(p3, 0, 3, nameof(p3)); - DebugGuard.MustBeBetweenOrEqualTo(p2, 0, 3, nameof(p2)); - DebugGuard.MustBeBetweenOrEqualTo(p1, 0, 3, nameof(p1)); - DebugGuard.MustBeBetweenOrEqualTo(p0, 0, 3, nameof(p0)); - - this.p3 = p3; - this.p2 = p2; - this.p1 = p1; - this.p0 = p0; - this.Control = SimdUtils.Shuffle.MmShuffle(p3, p2, p1, p0); - } + public DefaultPad3Shuffle4(byte control) + => this.Control = control; public byte Control { get; } + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) + => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, this.Control); + [MethodImpl(InliningOptions.ShortMethod)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte dBase = ref MemoryMarshal.GetReference(dest); - int p3 = this.p3; - int p2 = this.p2; - int p1 = this.p1; - int p0 = this.p0; + Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0); Span temp = stackalloc byte[4]; ref byte t = ref MemoryMarshal.GetReference(temp); ref uint tu = ref Unsafe.As(ref t); - for (int i = 0, j = 0; i < source.Length; i += 3, j += 4) + for (nuint i = 0, j = 0; i < (uint)source.Length; i += 3, j += 4) { - ref var s = ref Unsafe.Add(ref sBase, i); + ref byte s = ref Unsafe.Add(ref sBase, i); tu = Unsafe.As(ref s) | 0xFF000000; - Unsafe.Add(ref dBase, j) = Unsafe.Add(ref t, p0); + Unsafe.Add(ref dBase, j + 0) = Unsafe.Add(ref t, p0); Unsafe.Add(ref dBase, j + 1) = Unsafe.Add(ref t, p1); Unsafe.Add(ref dBase, j + 2) = Unsafe.Add(ref t, p2); Unsafe.Add(ref dBase, j + 3) = Unsafe.Add(ref t, p3); @@ -64,11 +50,9 @@ internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4 internal readonly struct XYZWPad3Shuffle4 : IPad3Shuffle4 { - public byte Control - { - [MethodImpl(InliningOptions.ShortMethod)] - get => SimdUtils.Shuffle.MmShuffle(3, 2, 1, 0); - } + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) + => HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle3210); [MethodImpl(InliningOptions.ShortMethod)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) @@ -76,7 +60,7 @@ internal readonly struct XYZWPad3Shuffle4 : IPad3Shuffle4 ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte dBase = ref MemoryMarshal.GetReference(dest); - ref byte sEnd = ref Unsafe.Add(ref sBase, source.Length); + ref byte sEnd = ref Unsafe.Add(ref sBase, (uint)source.Length); ref byte sLoopEnd = ref Unsafe.Subtract(ref sEnd, 4); while (Unsafe.IsAddressLessThan(ref sBase, ref sLoopEnd)) diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs index 9bee9d15ec..2cd586212e 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using static SixLabors.ImageSharp.SimdUtils; namespace SixLabors.ImageSharp; @@ -13,37 +14,26 @@ internal interface IShuffle3 : IComponentShuffle internal readonly struct DefaultShuffle3 : IShuffle3 { - private readonly byte p2; - private readonly byte p1; - private readonly byte p0; - - public DefaultShuffle3(byte p2, byte p1, byte p0) - { - DebugGuard.MustBeBetweenOrEqualTo(p2, 0, 2, nameof(p2)); - DebugGuard.MustBeBetweenOrEqualTo(p1, 0, 2, nameof(p1)); - DebugGuard.MustBeBetweenOrEqualTo(p0, 0, 2, nameof(p0)); - - this.p2 = p2; - this.p1 = p1; - this.p0 = p0; - this.Control = SimdUtils.Shuffle.MmShuffle(3, p2, p1, p0); - } + public DefaultShuffle3(byte control) + => this.Control = control; public byte Control { get; } + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) + => HwIntrinsics.Shuffle3Reduce(ref source, ref dest, this.Control); + [MethodImpl(InliningOptions.ShortMethod)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte dBase = ref MemoryMarshal.GetReference(dest); - int p2 = this.p2; - int p1 = this.p1; - int p0 = this.p0; + Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0); - for (int i = 0; i < source.Length; i += 3) + for (nuint i = 0; i < (uint)source.Length; i += 3) { - Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, p0 + i); + Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + i); Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i); Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i); } diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs index 90b77b568e..5e82973e33 100644 --- a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs +++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using static SixLabors.ImageSharp.SimdUtils; namespace SixLabors.ImageSharp; @@ -13,38 +14,26 @@ internal interface IShuffle4Slice3 : IComponentShuffle internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3 { - private readonly byte p2; - private readonly byte p1; - private readonly byte p0; - - public DefaultShuffle4Slice3(byte p3, byte p2, byte p1, byte p0) - { - DebugGuard.MustBeBetweenOrEqualTo(p3, 0, 3, nameof(p3)); - DebugGuard.MustBeBetweenOrEqualTo(p2, 0, 3, nameof(p2)); - DebugGuard.MustBeBetweenOrEqualTo(p1, 0, 3, nameof(p1)); - DebugGuard.MustBeBetweenOrEqualTo(p0, 0, 3, nameof(p0)); - - this.p2 = p2; - this.p1 = p1; - this.p0 = p0; - this.Control = SimdUtils.Shuffle.MmShuffle(p3, p2, p1, p0); - } + public DefaultShuffle4Slice3(byte control) + => this.Control = control; public byte Control { get; } + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) + => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, this.Control); + [MethodImpl(InliningOptions.ShortMethod)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref byte sBase = ref MemoryMarshal.GetReference(source); ref byte dBase = ref MemoryMarshal.GetReference(dest); - int p2 = this.p2; - int p1 = this.p1; - int p0 = this.p0; + Shuffle.InverseMMShuffle(this.Control, out _, out uint p2, out uint p1, out uint p0); - for (int i = 0, j = 0; i < dest.Length; i += 3, j += 4) + for (nuint i = 0, j = 0; i < (uint)dest.Length; i += 3, j += 4) { - Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, p0 + j); + Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + j); Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + j); Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + j); } @@ -53,11 +42,9 @@ internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3 internal readonly struct XYZWShuffle4Slice3 : IShuffle4Slice3 { - public byte Control - { - [MethodImpl(InliningOptions.ShortMethod)] - get => SimdUtils.Shuffle.MmShuffle(3, 2, 1, 0); - } + [MethodImpl(InliningOptions.ShortMethod)] + public void ShuffleReduce(ref ReadOnlySpan source, ref Span dest) + => HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, Shuffle.MMShuffle3210); [MethodImpl(InliningOptions.ShortMethod)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) @@ -65,9 +52,9 @@ internal readonly struct XYZWShuffle4Slice3 : IShuffle4Slice3 ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref Byte3 dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); - int n = source.Length / 4; - int m = Numerics.Modulo4(n); - int u = n - m; + nint n = (nint)(uint)source.Length / 4; + nint m = Numerics.Modulo4(n); + nint u = n - m; ref uint sLoopEnd = ref Unsafe.Add(ref sBase, u); ref uint sEnd = ref Unsafe.Add(ref sBase, n); diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs index 2014a2a35b..ac122fc7d4 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs @@ -97,12 +97,12 @@ internal static partial class SimdUtils { VerifySpanInput(source, dest, Vector.Count); - int n = dest.Length / Vector.Count; + nuint n = dest.VectorCount(); ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { Vector b = Unsafe.Add(ref sourceBase, i); @@ -132,13 +132,13 @@ internal static partial class SimdUtils { VerifySpanInput(source, dest, Vector.Count); - int n = dest.Length / Vector.Count; + nuint n = dest.VectorCount(); ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { ref Vector s = ref Unsafe.Add(ref sourceBase, i * 4); diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs index 84760f2815..a551cebd05 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs @@ -71,7 +71,7 @@ internal static partial class SimdUtils { VerifySpanInput(source, dest, 4); - int count = dest.Length / 4; + uint count = (uint)dest.Length / 4; if (count == 0) { return; @@ -83,7 +83,7 @@ internal static partial class SimdUtils const float scale = 1f / 255f; Vector4 d = default; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < count; i++) { ref ByteVector4 s = ref Unsafe.Add(ref sBase, i); d.X = s.X; @@ -105,7 +105,7 @@ internal static partial class SimdUtils { VerifySpanInput(source, dest, 4); - int count = source.Length / 4; + uint count = (uint)source.Length / 4; if (count == 0) { return; @@ -117,7 +117,7 @@ internal static partial class SimdUtils var half = new Vector4(0.5f); var maxBytes = new Vector4(255f); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < count; i++) { Vector4 s = Unsafe.Add(ref sBase, i); s *= maxBytes; diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index 4bc0040c67..e87872a707 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; using SixLabors.ImageSharp.PixelFormats; @@ -13,33 +14,38 @@ internal static partial class SimdUtils { public static class HwIntrinsics { - public static ReadOnlySpan PermuteMaskDeinterleave8x32 => new byte[] { 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0 }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] // too much IL for JIT to inline, so give a hint + public static Vector256 PermuteMaskDeinterleave8x32() => Vector256.Create(0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0).AsInt32(); - public static ReadOnlySpan PermuteMaskEvenOdd8x32 => new byte[] { 0, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0 }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 PermuteMaskEvenOdd8x32() => Vector256.Create(0, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0).AsUInt32(); - public static ReadOnlySpan PermuteMaskSwitchInnerDWords8x32 => new byte[] { 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0 }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 PermuteMaskSwitchInnerDWords8x32() => Vector256.Create(0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0).AsUInt32(); - private static ReadOnlySpan MoveFirst24BytesToSeparateLanes => new byte[] { 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0 }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 MoveFirst24BytesToSeparateLanes() => Vector256.Create(0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0).AsUInt32(); - internal static ReadOnlySpan ExtractRgb => new byte[] { 0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector256 ExtractRgb() => Vector256.Create(0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF); - private static ReadOnlySpan ShuffleMaskPad4Nx16 => new byte[] { 0, 1, 2, 0x80, 3, 4, 5, 0x80, 6, 7, 8, 0x80, 9, 10, 11, 0x80 }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ShuffleMaskPad4Nx16() => Vector128.Create(0, 1, 2, 0x80, 3, 4, 5, 0x80, 6, 7, 8, 0x80, 9, 10, 11, 0x80); - private static ReadOnlySpan ShuffleMaskSlice4Nx16 => new byte[] { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 0x80, 0x80, 0x80, 0x80 }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 ShuffleMaskSlice4Nx16() => Vector128.Create(0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 0x80, 0x80, 0x80, 0x80); - private static ReadOnlySpan ShuffleMaskShiftAlpha => - new byte[] - { - 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15, - 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15 - }; +#pragma warning disable SA1003, SA1116, SA1117 // Parameters should be on same line or separate lines + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 ShuffleMaskShiftAlpha() => Vector256.Create((byte) + 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15, + 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15); - public static ReadOnlySpan PermuteMaskShiftAlpha8x32 => - new byte[] - { - 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, - 5, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0 - }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 PermuteMaskShiftAlpha8x32() => Vector256.Create( + 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, + 5, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0).AsUInt32(); +#pragma warning restore SA1003, SA1116, SA1117 // Parameters should be on same line or separate lines /// /// Shuffle single-precision (32-bit) floating-point elements in @@ -159,7 +165,7 @@ internal static partial class SimdUtils int remainder = source.Length % (Vector128.Count * 3); int sourceCount = source.Length - remainder; - int destCount = sourceCount * 4 / 3; + int destCount = (int)((uint)sourceCount * 4 / 3); if (sourceCount > 0) { @@ -189,10 +195,10 @@ internal static partial class SimdUtils { if (Ssse3.IsSupported) { - int remainder = source.Length % (Vector128.Count * 4); + int remainder = source.Length & ((Vector128.Count * 4) - 1); // bit-hack for modulo int sourceCount = source.Length - remainder; - int destCount = sourceCount * 3 / 4; + int destCount = (int)((uint)sourceCount * 3 / 4); if (sourceCount > 0) { @@ -221,11 +227,11 @@ internal static partial class SimdUtils ref Vector256 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - int n = dest.Length / Vector256.Count; - int m = Numerics.Modulo4(n); - int u = n - m; + nint n = (nint)dest.Vector256Count(); + nint m = Numerics.Modulo4(n); + nint u = n - m; - for (int i = 0; i < u; i += 4) + for (nint i = 0; i < u; i += 4) { ref Vector256 vd0 = ref Unsafe.Add(ref destBase, i); ref Vector256 vs0 = ref Unsafe.Add(ref sourceBase, i); @@ -238,7 +244,7 @@ internal static partial class SimdUtils if (m > 0) { - for (int i = u; i < n; i++) + for (nint i = u; i < n; i++) { Unsafe.Add(ref destBase, i) = Avx.Permute(Unsafe.Add(ref sourceBase, i), control); } @@ -253,11 +259,11 @@ internal static partial class SimdUtils ref Vector128 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - int n = dest.Length / Vector128.Count; - int m = Numerics.Modulo4(n); - int u = n - m; + nint n = (nint)((uint)dest.Length / (uint)Vector128.Count); + nint m = Numerics.Modulo4(n); + nint u = n - m; - for (int i = 0; i < u; i += 4) + for (nint i = 0; i < u; i += 4) { ref Vector128 vd0 = ref Unsafe.Add(ref destBase, i); ref Vector128 vs0 = ref Unsafe.Add(ref sourceBase, i); @@ -276,7 +282,7 @@ internal static partial class SimdUtils if (m > 0) { - for (int i = u; i < n; i++) + for (nint i = u; i < n; i++) { Vector128 vs = Unsafe.Add(ref sourceBase, i); Unsafe.Add(ref destBase, i) = Sse.Shuffle(vs, vs, control); @@ -297,7 +303,7 @@ internal static partial class SimdUtils // shuffle controls to add to the library. // We can add static ROS instances if need be in the future. Span bytes = stackalloc byte[Vector256.Count]; - Shuffle.MmShuffleSpan(ref bytes, control); + Shuffle.MMShuffleSpan(ref bytes, control); Vector256 vshuffle = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); ref Vector256 sourceBase = @@ -306,11 +312,11 @@ internal static partial class SimdUtils ref Vector256 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - int n = dest.Length / Vector256.Count; - int m = Numerics.Modulo4(n); - int u = n - m; + nint n = (nint)((uint)dest.Length / (uint)Vector256.Count); + nint m = Numerics.Modulo4(n); + nint u = n - m; - for (int i = 0; i < u; i += 4) + for (nint i = 0; i < u; i += 4) { ref Vector256 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector256 vd0 = ref Unsafe.Add(ref destBase, i); @@ -323,7 +329,7 @@ internal static partial class SimdUtils if (m > 0) { - for (int i = u; i < n; i++) + for (nint i = u; i < n; i++) { Unsafe.Add(ref destBase, i) = Avx2.Shuffle(Unsafe.Add(ref sourceBase, i), vshuffle); } @@ -333,7 +339,7 @@ internal static partial class SimdUtils { // Ssse3 Span bytes = stackalloc byte[Vector128.Count]; - Shuffle.MmShuffleSpan(ref bytes, control); + Shuffle.MMShuffleSpan(ref bytes, control); Vector128 vshuffle = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); ref Vector128 sourceBase = @@ -342,11 +348,11 @@ internal static partial class SimdUtils ref Vector128 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - int n = dest.Length / Vector128.Count; - int m = Numerics.Modulo4(n); - int u = n - m; + nint n = (nint)((uint)dest.Length / (uint)Vector128.Count); + nint m = Numerics.Modulo4(n); + nint u = n - m; - for (int i = 0; i < u; i += 4) + for (nint i = 0; i < u; i += 4) { ref Vector128 vs0 = ref Unsafe.Add(ref sourceBase, i); ref Vector128 vd0 = ref Unsafe.Add(ref destBase, i); @@ -359,7 +365,7 @@ internal static partial class SimdUtils if (m > 0) { - for (int i = u; i < n; i++) + for (nint i = u; i < n; i++) { Unsafe.Add(ref destBase, i) = Ssse3.Shuffle(Unsafe.Add(ref sourceBase, i), vshuffle); } @@ -375,14 +381,12 @@ internal static partial class SimdUtils { if (Ssse3.IsSupported) { - ref byte vmaskBase = ref MemoryMarshal.GetReference(ShuffleMaskPad4Nx16); - Vector128 vmask = Unsafe.As>(ref vmaskBase); - ref byte vmaskoBase = ref MemoryMarshal.GetReference(ShuffleMaskSlice4Nx16); - Vector128 vmasko = Unsafe.As>(ref vmaskoBase); + Vector128 vmask = ShuffleMaskPad4Nx16(); + Vector128 vmasko = ShuffleMaskSlice4Nx16(); Vector128 vmaske = Ssse3.AlignRight(vmasko, vmasko, 12); Span bytes = stackalloc byte[Vector128.Count]; - Shuffle.MmShuffleSpan(ref bytes, control); + Shuffle.MMShuffleSpan(ref bytes, control); Vector128 vshuffle = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); ref Vector128 sourceBase = @@ -391,9 +395,9 @@ internal static partial class SimdUtils ref Vector128 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - int n = source.Length / Vector128.Count; + nuint n = source.Vector128Count(); - for (int i = 0; i < n; i += 3) + for (nuint i = 0; i < n; i += 3) { ref Vector128 vs = ref Unsafe.Add(ref sourceBase, i); @@ -440,12 +444,11 @@ internal static partial class SimdUtils { if (Ssse3.IsSupported) { - ref byte vmaskBase = ref MemoryMarshal.GetReference(ShuffleMaskPad4Nx16); - Vector128 vmask = Unsafe.As>(ref vmaskBase); + Vector128 vmask = ShuffleMaskPad4Nx16(); Vector128 vfill = Vector128.Create(0xff000000ff000000ul).AsByte(); Span bytes = stackalloc byte[Vector128.Count]; - Shuffle.MmShuffleSpan(ref bytes, control); + Shuffle.MMShuffleSpan(ref bytes, control); Vector128 vshuffle = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); ref Vector128 sourceBase = @@ -454,9 +457,9 @@ internal static partial class SimdUtils ref Vector128 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - int n = source.Length / Vector128.Count; + nuint n = source.Vector128Count(); - for (int i = 0, j = 0; i < n; i += 3, j += 4) + for (nuint i = 0, j = 0; i < n; i += 3, j += 4) { ref Vector128 v0 = ref Unsafe.Add(ref sourceBase, i); Vector128 v1 = Unsafe.Add(ref v0, 1); @@ -484,12 +487,11 @@ internal static partial class SimdUtils { if (Ssse3.IsSupported) { - ref byte vmaskoBase = ref MemoryMarshal.GetReference(ShuffleMaskSlice4Nx16); - Vector128 vmasko = Unsafe.As>(ref vmaskoBase); + Vector128 vmasko = ShuffleMaskSlice4Nx16(); Vector128 vmaske = Ssse3.AlignRight(vmasko, vmasko, 12); Span bytes = stackalloc byte[Vector128.Count]; - Shuffle.MmShuffleSpan(ref bytes, control); + Shuffle.MMShuffleSpan(ref bytes, control); Vector128 vshuffle = Unsafe.As>(ref MemoryMarshal.GetReference(bytes)); ref Vector128 sourceBase = @@ -498,9 +500,9 @@ internal static partial class SimdUtils ref Vector128 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - int n = source.Length / Vector128.Count; + nuint n = source.Vector128Count(); - for (int i = 0, j = 0; i < n; i += 4, j += 3) + for (nuint i = 0, j = 0; i < n; i += 4, j += 3) { ref Vector128 vs = ref Unsafe.Add(ref sourceBase, i); @@ -532,7 +534,8 @@ internal static partial class SimdUtils } /// - /// Performs a multiplication and an addition of the . + /// Performs a multiplication and an addition of the . + /// TODO: Fix. The arguments are in a different order to the FMA intrinsic. /// /// ret = (vm0 * vm1) + va /// The vector to add to the intermediate result. @@ -541,42 +544,89 @@ internal static partial class SimdUtils /// The . [MethodImpl(InliningOptions.AlwaysInline)] public static Vector256 MultiplyAdd( - in Vector256 va, - in Vector256 vm0, - in Vector256 vm1) + Vector256 va, + Vector256 vm0, + Vector256 vm1) { if (Fma.IsSupported) { return Fma.MultiplyAdd(vm1, vm0, va); } - else + + return Avx.Add(Avx.Multiply(vm0, vm1), va); + } + + /// + /// Performs a multiplication and an addition of the . + /// TODO: Fix. The arguments are in a different order to the FMA intrinsic. + /// + /// ret = (vm0 * vm1) + va + /// The vector to add to the intermediate result. + /// The first vector to multiply. + /// The second vector to multiply. + /// The . + [MethodImpl(InliningOptions.AlwaysInline)] + public static Vector128 MultiplyAdd( + Vector128 va, + Vector128 vm0, + Vector128 vm1) + { + if (Fma.IsSupported) { - return Avx.Add(Avx.Multiply(vm0, vm1), va); + return Fma.MultiplyAdd(vm1, vm0, va); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.Add(AdvSimd.Multiply(vm0, vm1), va); } + + return Sse.Add(Sse.Multiply(vm0, vm1), va); } /// - /// Performs a multiplication and a substraction of the . + /// Performs a multiplication and a subtraction of the . + /// TODO: Fix. The arguments are in a different order to the FMA intrinsic. /// /// ret = (vm0 * vm1) - vs - /// The vector to substract from the intermediate result. + /// The vector to subtract from the intermediate result. /// The first vector to multiply. /// The second vector to multiply. /// The . [MethodImpl(InliningOptions.ShortMethod)] - public static Vector256 MultiplySubstract( - in Vector256 vs, - in Vector256 vm0, - in Vector256 vm1) + public static Vector256 MultiplySubtract( + Vector256 vs, + Vector256 vm0, + Vector256 vm1) { if (Fma.IsSupported) { return Fma.MultiplySubtract(vm1, vm0, vs); } - else + + return Avx.Subtract(Avx.Multiply(vm0, vm1), vs); + } + + /// + /// Performs a multiplication and a negated addition of the . + /// + /// ret = c - (a * b) + /// The first vector to multiply. + /// The second vector to multiply. + /// The vector to add negated to the intermediate result. + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static Vector256 MultiplyAddNegated( + Vector256 a, + Vector256 b, + Vector256 c) + { + if (Fma.IsSupported) { - return Avx.Subtract(Avx.Multiply(vm0, vm1), vs); + return Fma.MultiplyAddNegated(a, b, c); } + + return Avx.Subtract(c, Avx.Multiply(a, b)); } /// @@ -630,16 +680,16 @@ internal static partial class SimdUtils { VerifySpanInput(source, dest, Vector256.Count); - int n = dest.Length / Vector256.Count; + nuint n = dest.Vector256Count(); ref Vector256 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - var scale = Vector256.Create(1 / (float)byte.MaxValue); + Vector256 scale = Vector256.Create(1 / (float)byte.MaxValue); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { - int si = Vector256.Count * i; + nuint si = (uint)Vector256.Count * i; Vector256 i0 = Avx2.ConvertToVector256Int32(sourceBase + si); Vector256 i1 = Avx2.ConvertToVector256Int32(sourceBase + si + Vector256.Count); Vector256 i2 = Avx2.ConvertToVector256Int32(sourceBase + si + (Vector256.Count * 2)); @@ -663,17 +713,17 @@ internal static partial class SimdUtils // Sse VerifySpanInput(source, dest, Vector128.Count); - int n = dest.Length / Vector128.Count; + nuint n = dest.Vector128Count(); ref Vector128 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - var scale = Vector128.Create(1 / (float)byte.MaxValue); + Vector128 scale = Vector128.Create(1 / (float)byte.MaxValue); Vector128 zero = Vector128.Zero; - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { - int si = Vector128.Count * i; + nuint si = (uint)Vector128.Count * i; Vector128 i0, i1, i2, i3; if (Sse41.IsSupported) @@ -762,7 +812,7 @@ internal static partial class SimdUtils { VerifySpanInput(source, dest, Vector256.Count); - int n = dest.Length / Vector256.Count; + nuint n = dest.Vector256Count(); ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); @@ -770,11 +820,10 @@ internal static partial class SimdUtils ref Vector256 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - var scale = Vector256.Create((float)byte.MaxValue); - ref byte maskBase = ref MemoryMarshal.GetReference(PermuteMaskDeinterleave8x32); - Vector256 mask = Unsafe.As>(ref maskBase); + Vector256 scale = Vector256.Create((float)byte.MaxValue); + Vector256 mask = PermuteMaskDeinterleave8x32(); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { ref Vector256 s = ref Unsafe.Add(ref sourceBase, i * 4); @@ -801,7 +850,7 @@ internal static partial class SimdUtils // Sse VerifySpanInput(source, dest, Vector128.Count); - int n = dest.Length / Vector128.Count; + nuint n = dest.Vector128Count(); ref Vector128 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); @@ -809,9 +858,9 @@ internal static partial class SimdUtils ref Vector128 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); - var scale = Vector128.Create((float)byte.MaxValue); + Vector128 scale = Vector128.Create((float)byte.MaxValue); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { ref Vector128 s = ref Unsafe.Add(ref sourceBase, i * 4); @@ -844,18 +893,16 @@ internal static partial class SimdUtils ref Vector256 bBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(blueChannel)); ref byte dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); - int count = redChannel.Length / Vector256.Count; + nuint count = redChannel.Vector256Count(); - ref byte control1Bytes = ref MemoryMarshal.GetReference(PermuteMaskEvenOdd8x32); - Vector256 control1 = Unsafe.As>(ref control1Bytes); + Vector256 control1 = PermuteMaskEvenOdd8x32(); - ref byte control2Bytes = ref MemoryMarshal.GetReference(PermuteMaskShiftAlpha8x32); - Vector256 control2 = Unsafe.As>(ref control2Bytes); - var a = Vector256.Create((byte)255); + Vector256 control2 = PermuteMaskShiftAlpha8x32(); + Vector256 a = Vector256.Create((byte)255); - Vector256 shuffleAlpha = Unsafe.As>(ref MemoryMarshal.GetReference(ShuffleMaskShiftAlpha)); + Vector256 shuffleAlpha = ShuffleMaskShiftAlpha(); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < count; i++) { Vector256 r0 = Unsafe.Add(ref rBase, i); Vector256 g0 = Unsafe.Add(ref gBase, i); @@ -898,7 +945,7 @@ internal static partial class SimdUtils Unsafe.As>(ref d4) = rgb4; } - int slice = count * Vector256.Count; + int slice = (int)count * Vector256.Count; redChannel = redChannel[slice..]; greenChannel = greenChannel[slice..]; blueChannel = blueChannel[slice..]; @@ -916,12 +963,11 @@ internal static partial class SimdUtils ref Vector256 bBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(blueChannel)); ref Vector256 dBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); - int count = redChannel.Length / Vector256.Count; - ref byte control1Bytes = ref MemoryMarshal.GetReference(PermuteMaskEvenOdd8x32); - Vector256 control1 = Unsafe.As>(ref control1Bytes); - var a = Vector256.Create((byte)255); + nuint count = redChannel.Vector256Count(); + Vector256 control1 = PermuteMaskEvenOdd8x32(); + Vector256 a = Vector256.Create((byte)255); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < count; i++) { Vector256 r0 = Unsafe.Add(ref rBase, i); Vector256 g0 = Unsafe.Add(ref gBase, i); @@ -950,7 +996,7 @@ internal static partial class SimdUtils Unsafe.Add(ref d0, 3) = rgb4; } - int slice = count * Vector256.Count; + int slice = (int)count * Vector256.Count; redChannel = redChannel[slice..]; greenChannel = greenChannel[slice..]; blueChannel = blueChannel[slice..]; @@ -968,16 +1014,16 @@ internal static partial class SimdUtils ref Vector256 destGRef = ref Unsafe.As>(ref MemoryMarshal.GetReference(greenChannel)); ref Vector256 destBRef = ref Unsafe.As>(ref MemoryMarshal.GetReference(blueChannel)); - Vector256 extractToLanesMask = Unsafe.As>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes)); - Vector256 extractRgbMask = Unsafe.As>(ref MemoryMarshal.GetReference(ExtractRgb)); + Vector256 extractToLanesMask = MoveFirst24BytesToSeparateLanes(); + Vector256 extractRgbMask = ExtractRgb(); Vector256 rgb, rg, bx; Vector256 r, g, b; const int bytesPerRgbStride = 24; - int count = (int)((uint)source.Length / 8); - for (int i = 0; i < count; i++) + nuint count = (uint)source.Length / 8; + for (nuint i = 0; i < count; i++) { - rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * i)).AsUInt32(), extractToLanesMask).AsByte(); + rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (uint)(bytesPerRgbStride * i)).AsUInt32(), extractToLanesMask).AsByte(); rgb = Avx2.Shuffle(rgb, extractRgbMask); @@ -993,7 +1039,7 @@ internal static partial class SimdUtils Unsafe.Add(ref destBRef, i) = b; } - int sliceCount = count * 8; + int sliceCount = (int)(count * 8); redChannel = redChannel.Slice(sliceCount); greenChannel = greenChannel.Slice(sliceCount); blueChannel = blueChannel.Slice(sliceCount); diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs index a01a32f5e1..f471d0231b 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs @@ -86,8 +86,8 @@ internal static partial class SimdUtils ref ByteTuple4 b = ref Unsafe.As(ref MemoryMarshal.GetReference(blueChannel)); ref Rgb24 rgb = ref MemoryMarshal.GetReference(destination); - int count = redChannel.Length / 4; - for (int i = 0; i < count; i++) + nuint count = (uint)redChannel.Length / 4; + for (nuint i = 0; i < count; i++) { ref Rgb24 d0 = ref Unsafe.Add(ref rgb, i * 4); ref Rgb24 d1 = ref Unsafe.Add(ref d0, 1); @@ -115,7 +115,7 @@ internal static partial class SimdUtils d3.B = bb.V3; } - int finished = count * 4; + int finished = (int)(count * 4); redChannel = redChannel[finished..]; greenChannel = greenChannel[finished..]; blueChannel = blueChannel[finished..]; @@ -133,9 +133,9 @@ internal static partial class SimdUtils ref ByteTuple4 b = ref Unsafe.As(ref MemoryMarshal.GetReference(blueChannel)); ref Rgba32 rgb = ref MemoryMarshal.GetReference(destination); - int count = redChannel.Length / 4; + nuint count = (uint)redChannel.Length / 4; destination.Fill(new Rgba32(0, 0, 0, 255)); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < count; i++) { ref Rgba32 d0 = ref Unsafe.Add(ref rgb, i * 4); ref Rgba32 d1 = ref Unsafe.Add(ref d0, 1); @@ -163,7 +163,7 @@ internal static partial class SimdUtils d3.B = bb.V3; } - int finished = count * 4; + int finished = (int)(count * 4); redChannel = redChannel[finished..]; greenChannel = greenChannel[finished..]; blueChannel = blueChannel[finished..]; @@ -181,7 +181,7 @@ internal static partial class SimdUtils ref byte b = ref MemoryMarshal.GetReference(blueChannel); ref Rgb24 rgb = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < destination.Length; i++) + for (nuint i = 0; i < (uint)destination.Length; i++) { ref Rgb24 d = ref Unsafe.Add(ref rgb, i); d.R = Unsafe.Add(ref r, i); @@ -201,7 +201,7 @@ internal static partial class SimdUtils ref byte b = ref MemoryMarshal.GetReference(blueChannel); ref Rgba32 rgba = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < destination.Length; i++) + for (nuint i = 0; i < (uint)destination.Length; i++) { ref Rgba32 d = ref Unsafe.Add(ref rgba, i); d.R = Unsafe.Add(ref r, i); @@ -226,7 +226,7 @@ internal static partial class SimdUtils ref float b = ref MemoryMarshal.GetReference(blueChannel); ref Rgb24 rgb = ref MemoryMarshal.GetReference(source); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref Rgb24 src = ref Unsafe.Add(ref rgb, i); Unsafe.Add(ref r, i) = src.R; diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs index b91dc2fad2..c1437c05e6 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs @@ -37,6 +37,7 @@ internal static partial class SimdUtils /// Shuffle 8-bit integers within 128-bit lanes in /// using the control and store the results in . /// + /// The type of shuffle struct. /// The source span of bytes. /// The destination span of bytes. /// The type of shuffle to perform. @@ -49,7 +50,7 @@ internal static partial class SimdUtils { VerifyShuffle4SpanInput(source, dest); - HwIntrinsics.Shuffle4Reduce(ref source, ref dest, shuffle.Control); + shuffle.ShuffleReduce(ref source, ref dest); // Deal with the remainder: if (source.Length > 0) @@ -62,6 +63,7 @@ internal static partial class SimdUtils /// Shuffle 8-bit integer triplets within 128-bit lanes in /// using the control and store the results in . /// + /// The type of shuffle struct. /// The source span of bytes. /// The destination span of bytes. /// The type of shuffle to perform. @@ -75,7 +77,7 @@ internal static partial class SimdUtils // Source length should be smaller than dest length, and divisible by 3. VerifyShuffle3SpanInput(source, dest); - HwIntrinsics.Shuffle3Reduce(ref source, ref dest, shuffle.Control); + shuffle.ShuffleReduce(ref source, ref dest); // Deal with the remainder: if (source.Length > 0) @@ -88,6 +90,7 @@ internal static partial class SimdUtils /// Pads then shuffles 8-bit integers within 128-bit lanes in /// using the control and store the results in . /// + /// The type of shuffle struct. /// The source span of bytes. /// The destination span of bytes. /// The type of shuffle to perform. @@ -100,7 +103,7 @@ internal static partial class SimdUtils { VerifyPad3Shuffle4SpanInput(source, dest); - HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, shuffle.Control); + shuffle.ShuffleReduce(ref source, ref dest); // Deal with the remainder: if (source.Length > 0) @@ -113,6 +116,7 @@ internal static partial class SimdUtils /// Shuffles then slices 8-bit integers within 128-bit lanes in /// using the control and store the results in . /// + /// The type of shuffle struct. /// The source span of bytes. /// The destination span of bytes. /// The type of shuffle to perform. @@ -125,7 +129,7 @@ internal static partial class SimdUtils { VerifyShuffle4Slice3SpanInput(source, dest); - HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, shuffle.Control); + shuffle.ShuffleReduce(ref source, ref dest); // Deal with the remainder: if (source.Length > 0) @@ -141,11 +145,11 @@ internal static partial class SimdUtils { ref float sBase = ref MemoryMarshal.GetReference(source); ref float dBase = ref MemoryMarshal.GetReference(dest); - Shuffle.InverseMmShuffle(control, out int p3, out int p2, out int p1, out int p0); + Shuffle.InverseMMShuffle(control, out uint p3, out uint p2, out uint p1, out uint p0); - for (int i = 0; i < source.Length; i += 4) + for (nuint i = 0; i < (uint)source.Length; i += 4) { - Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, p0 + i); + Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + i); Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i); Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i); Unsafe.Add(ref dBase, i + 3) = Unsafe.Add(ref sBase, p3 + i); @@ -153,7 +157,7 @@ internal static partial class SimdUtils } [Conditional("DEBUG")] - private static void VerifyShuffle4SpanInput(ReadOnlySpan source, Span dest) + internal static void VerifyShuffle4SpanInput(ReadOnlySpan source, Span dest) where T : struct { DebugGuard.IsTrue( @@ -222,25 +226,282 @@ internal static partial class SimdUtils public static class Shuffle { + public const byte MMShuffle0000 = 0b00000000; + public const byte MMShuffle0001 = 0b00000001; + public const byte MMShuffle0002 = 0b00000010; + public const byte MMShuffle0003 = 0b00000011; + public const byte MMShuffle0010 = 0b00000100; + public const byte MMShuffle0011 = 0b00000101; + public const byte MMShuffle0012 = 0b00000110; + public const byte MMShuffle0013 = 0b00000111; + public const byte MMShuffle0020 = 0b00001000; + public const byte MMShuffle0021 = 0b00001001; + public const byte MMShuffle0022 = 0b00001010; + public const byte MMShuffle0023 = 0b00001011; + public const byte MMShuffle0030 = 0b00001100; + public const byte MMShuffle0031 = 0b00001101; + public const byte MMShuffle0032 = 0b00001110; + public const byte MMShuffle0033 = 0b00001111; + public const byte MMShuffle0100 = 0b00010000; + public const byte MMShuffle0101 = 0b00010001; + public const byte MMShuffle0102 = 0b00010010; + public const byte MMShuffle0103 = 0b00010011; + public const byte MMShuffle0110 = 0b00010100; + public const byte MMShuffle0111 = 0b00010101; + public const byte MMShuffle0112 = 0b00010110; + public const byte MMShuffle0113 = 0b00010111; + public const byte MMShuffle0120 = 0b00011000; + public const byte MMShuffle0121 = 0b00011001; + public const byte MMShuffle0122 = 0b00011010; + public const byte MMShuffle0123 = 0b00011011; + public const byte MMShuffle0130 = 0b00011100; + public const byte MMShuffle0131 = 0b00011101; + public const byte MMShuffle0132 = 0b00011110; + public const byte MMShuffle0133 = 0b00011111; + public const byte MMShuffle0200 = 0b00100000; + public const byte MMShuffle0201 = 0b00100001; + public const byte MMShuffle0202 = 0b00100010; + public const byte MMShuffle0203 = 0b00100011; + public const byte MMShuffle0210 = 0b00100100; + public const byte MMShuffle0211 = 0b00100101; + public const byte MMShuffle0212 = 0b00100110; + public const byte MMShuffle0213 = 0b00100111; + public const byte MMShuffle0220 = 0b00101000; + public const byte MMShuffle0221 = 0b00101001; + public const byte MMShuffle0222 = 0b00101010; + public const byte MMShuffle0223 = 0b00101011; + public const byte MMShuffle0230 = 0b00101100; + public const byte MMShuffle0231 = 0b00101101; + public const byte MMShuffle0232 = 0b00101110; + public const byte MMShuffle0233 = 0b00101111; + public const byte MMShuffle0300 = 0b00110000; + public const byte MMShuffle0301 = 0b00110001; + public const byte MMShuffle0302 = 0b00110010; + public const byte MMShuffle0303 = 0b00110011; + public const byte MMShuffle0310 = 0b00110100; + public const byte MMShuffle0311 = 0b00110101; + public const byte MMShuffle0312 = 0b00110110; + public const byte MMShuffle0313 = 0b00110111; + public const byte MMShuffle0320 = 0b00111000; + public const byte MMShuffle0321 = 0b00111001; + public const byte MMShuffle0322 = 0b00111010; + public const byte MMShuffle0323 = 0b00111011; + public const byte MMShuffle0330 = 0b00111100; + public const byte MMShuffle0331 = 0b00111101; + public const byte MMShuffle0332 = 0b00111110; + public const byte MMShuffle0333 = 0b00111111; + public const byte MMShuffle1000 = 0b01000000; + public const byte MMShuffle1001 = 0b01000001; + public const byte MMShuffle1002 = 0b01000010; + public const byte MMShuffle1003 = 0b01000011; + public const byte MMShuffle1010 = 0b01000100; + public const byte MMShuffle1011 = 0b01000101; + public const byte MMShuffle1012 = 0b01000110; + public const byte MMShuffle1013 = 0b01000111; + public const byte MMShuffle1020 = 0b01001000; + public const byte MMShuffle1021 = 0b01001001; + public const byte MMShuffle1022 = 0b01001010; + public const byte MMShuffle1023 = 0b01001011; + public const byte MMShuffle1030 = 0b01001100; + public const byte MMShuffle1031 = 0b01001101; + public const byte MMShuffle1032 = 0b01001110; + public const byte MMShuffle1033 = 0b01001111; + public const byte MMShuffle1100 = 0b01010000; + public const byte MMShuffle1101 = 0b01010001; + public const byte MMShuffle1102 = 0b01010010; + public const byte MMShuffle1103 = 0b01010011; + public const byte MMShuffle1110 = 0b01010100; + public const byte MMShuffle1111 = 0b01010101; + public const byte MMShuffle1112 = 0b01010110; + public const byte MMShuffle1113 = 0b01010111; + public const byte MMShuffle1120 = 0b01011000; + public const byte MMShuffle1121 = 0b01011001; + public const byte MMShuffle1122 = 0b01011010; + public const byte MMShuffle1123 = 0b01011011; + public const byte MMShuffle1130 = 0b01011100; + public const byte MMShuffle1131 = 0b01011101; + public const byte MMShuffle1132 = 0b01011110; + public const byte MMShuffle1133 = 0b01011111; + public const byte MMShuffle1200 = 0b01100000; + public const byte MMShuffle1201 = 0b01100001; + public const byte MMShuffle1202 = 0b01100010; + public const byte MMShuffle1203 = 0b01100011; + public const byte MMShuffle1210 = 0b01100100; + public const byte MMShuffle1211 = 0b01100101; + public const byte MMShuffle1212 = 0b01100110; + public const byte MMShuffle1213 = 0b01100111; + public const byte MMShuffle1220 = 0b01101000; + public const byte MMShuffle1221 = 0b01101001; + public const byte MMShuffle1222 = 0b01101010; + public const byte MMShuffle1223 = 0b01101011; + public const byte MMShuffle1230 = 0b01101100; + public const byte MMShuffle1231 = 0b01101101; + public const byte MMShuffle1232 = 0b01101110; + public const byte MMShuffle1233 = 0b01101111; + public const byte MMShuffle1300 = 0b01110000; + public const byte MMShuffle1301 = 0b01110001; + public const byte MMShuffle1302 = 0b01110010; + public const byte MMShuffle1303 = 0b01110011; + public const byte MMShuffle1310 = 0b01110100; + public const byte MMShuffle1311 = 0b01110101; + public const byte MMShuffle1312 = 0b01110110; + public const byte MMShuffle1313 = 0b01110111; + public const byte MMShuffle1320 = 0b01111000; + public const byte MMShuffle1321 = 0b01111001; + public const byte MMShuffle1322 = 0b01111010; + public const byte MMShuffle1323 = 0b01111011; + public const byte MMShuffle1330 = 0b01111100; + public const byte MMShuffle1331 = 0b01111101; + public const byte MMShuffle1332 = 0b01111110; + public const byte MMShuffle1333 = 0b01111111; + public const byte MMShuffle2000 = 0b10000000; + public const byte MMShuffle2001 = 0b10000001; + public const byte MMShuffle2002 = 0b10000010; + public const byte MMShuffle2003 = 0b10000011; + public const byte MMShuffle2010 = 0b10000100; + public const byte MMShuffle2011 = 0b10000101; + public const byte MMShuffle2012 = 0b10000110; + public const byte MMShuffle2013 = 0b10000111; + public const byte MMShuffle2020 = 0b10001000; + public const byte MMShuffle2021 = 0b10001001; + public const byte MMShuffle2022 = 0b10001010; + public const byte MMShuffle2023 = 0b10001011; + public const byte MMShuffle2030 = 0b10001100; + public const byte MMShuffle2031 = 0b10001101; + public const byte MMShuffle2032 = 0b10001110; + public const byte MMShuffle2033 = 0b10001111; + public const byte MMShuffle2100 = 0b10010000; + public const byte MMShuffle2101 = 0b10010001; + public const byte MMShuffle2102 = 0b10010010; + public const byte MMShuffle2103 = 0b10010011; + public const byte MMShuffle2110 = 0b10010100; + public const byte MMShuffle2111 = 0b10010101; + public const byte MMShuffle2112 = 0b10010110; + public const byte MMShuffle2113 = 0b10010111; + public const byte MMShuffle2120 = 0b10011000; + public const byte MMShuffle2121 = 0b10011001; + public const byte MMShuffle2122 = 0b10011010; + public const byte MMShuffle2123 = 0b10011011; + public const byte MMShuffle2130 = 0b10011100; + public const byte MMShuffle2131 = 0b10011101; + public const byte MMShuffle2132 = 0b10011110; + public const byte MMShuffle2133 = 0b10011111; + public const byte MMShuffle2200 = 0b10100000; + public const byte MMShuffle2201 = 0b10100001; + public const byte MMShuffle2202 = 0b10100010; + public const byte MMShuffle2203 = 0b10100011; + public const byte MMShuffle2210 = 0b10100100; + public const byte MMShuffle2211 = 0b10100101; + public const byte MMShuffle2212 = 0b10100110; + public const byte MMShuffle2213 = 0b10100111; + public const byte MMShuffle2220 = 0b10101000; + public const byte MMShuffle2221 = 0b10101001; + public const byte MMShuffle2222 = 0b10101010; + public const byte MMShuffle2223 = 0b10101011; + public const byte MMShuffle2230 = 0b10101100; + public const byte MMShuffle2231 = 0b10101101; + public const byte MMShuffle2232 = 0b10101110; + public const byte MMShuffle2233 = 0b10101111; + public const byte MMShuffle2300 = 0b10110000; + public const byte MMShuffle2301 = 0b10110001; + public const byte MMShuffle2302 = 0b10110010; + public const byte MMShuffle2303 = 0b10110011; + public const byte MMShuffle2310 = 0b10110100; + public const byte MMShuffle2311 = 0b10110101; + public const byte MMShuffle2312 = 0b10110110; + public const byte MMShuffle2313 = 0b10110111; + public const byte MMShuffle2320 = 0b10111000; + public const byte MMShuffle2321 = 0b10111001; + public const byte MMShuffle2322 = 0b10111010; + public const byte MMShuffle2323 = 0b10111011; + public const byte MMShuffle2330 = 0b10111100; + public const byte MMShuffle2331 = 0b10111101; + public const byte MMShuffle2332 = 0b10111110; + public const byte MMShuffle2333 = 0b10111111; + public const byte MMShuffle3000 = 0b11000000; + public const byte MMShuffle3001 = 0b11000001; + public const byte MMShuffle3002 = 0b11000010; + public const byte MMShuffle3003 = 0b11000011; + public const byte MMShuffle3010 = 0b11000100; + public const byte MMShuffle3011 = 0b11000101; + public const byte MMShuffle3012 = 0b11000110; + public const byte MMShuffle3013 = 0b11000111; + public const byte MMShuffle3020 = 0b11001000; + public const byte MMShuffle3021 = 0b11001001; + public const byte MMShuffle3022 = 0b11001010; + public const byte MMShuffle3023 = 0b11001011; + public const byte MMShuffle3030 = 0b11001100; + public const byte MMShuffle3031 = 0b11001101; + public const byte MMShuffle3032 = 0b11001110; + public const byte MMShuffle3033 = 0b11001111; + public const byte MMShuffle3100 = 0b11010000; + public const byte MMShuffle3101 = 0b11010001; + public const byte MMShuffle3102 = 0b11010010; + public const byte MMShuffle3103 = 0b11010011; + public const byte MMShuffle3110 = 0b11010100; + public const byte MMShuffle3111 = 0b11010101; + public const byte MMShuffle3112 = 0b11010110; + public const byte MMShuffle3113 = 0b11010111; + public const byte MMShuffle3120 = 0b11011000; + public const byte MMShuffle3121 = 0b11011001; + public const byte MMShuffle3122 = 0b11011010; + public const byte MMShuffle3123 = 0b11011011; + public const byte MMShuffle3130 = 0b11011100; + public const byte MMShuffle3131 = 0b11011101; + public const byte MMShuffle3132 = 0b11011110; + public const byte MMShuffle3133 = 0b11011111; + public const byte MMShuffle3200 = 0b11100000; + public const byte MMShuffle3201 = 0b11100001; + public const byte MMShuffle3202 = 0b11100010; + public const byte MMShuffle3203 = 0b11100011; + public const byte MMShuffle3210 = 0b11100100; + public const byte MMShuffle3211 = 0b11100101; + public const byte MMShuffle3212 = 0b11100110; + public const byte MMShuffle3213 = 0b11100111; + public const byte MMShuffle3220 = 0b11101000; + public const byte MMShuffle3221 = 0b11101001; + public const byte MMShuffle3222 = 0b11101010; + public const byte MMShuffle3223 = 0b11101011; + public const byte MMShuffle3230 = 0b11101100; + public const byte MMShuffle3231 = 0b11101101; + public const byte MMShuffle3232 = 0b11101110; + public const byte MMShuffle3233 = 0b11101111; + public const byte MMShuffle3300 = 0b11110000; + public const byte MMShuffle3301 = 0b11110001; + public const byte MMShuffle3302 = 0b11110010; + public const byte MMShuffle3303 = 0b11110011; + public const byte MMShuffle3310 = 0b11110100; + public const byte MMShuffle3311 = 0b11110101; + public const byte MMShuffle3312 = 0b11110110; + public const byte MMShuffle3313 = 0b11110111; + public const byte MMShuffle3320 = 0b11111000; + public const byte MMShuffle3321 = 0b11111001; + public const byte MMShuffle3322 = 0b11111010; + public const byte MMShuffle3323 = 0b11111011; + public const byte MMShuffle3330 = 0b11111100; + public const byte MMShuffle3331 = 0b11111101; + public const byte MMShuffle3332 = 0b11111110; + public const byte MMShuffle3333 = 0b11111111; + [MethodImpl(InliningOptions.ShortMethod)] - public static byte MmShuffle(byte p3, byte p2, byte p1, byte p0) + public static byte MMShuffle(byte p3, byte p2, byte p1, byte p0) => (byte)((p3 << 6) | (p2 << 4) | (p1 << 2) | p0); [MethodImpl(InliningOptions.ShortMethod)] - public static void MmShuffleSpan(ref Span span, byte control) + public static void MMShuffleSpan(ref Span span, byte control) { - InverseMmShuffle( + InverseMMShuffle( control, - out int p3, - out int p2, - out int p1, - out int p0); + out uint p3, + out uint p2, + out uint p1, + out uint p0); ref byte spanBase = ref MemoryMarshal.GetReference(span); - for (int i = 0; i < span.Length; i += 4) + for (nuint i = 0; i < (uint)span.Length; i += 4) { - Unsafe.Add(ref spanBase, i) = (byte)(p0 + i); + Unsafe.Add(ref spanBase, i + 0) = (byte)(p0 + i); Unsafe.Add(ref spanBase, i + 1) = (byte)(p1 + i); Unsafe.Add(ref spanBase, i + 2) = (byte)(p2 + i); Unsafe.Add(ref spanBase, i + 3) = (byte)(p3 + i); @@ -248,17 +509,17 @@ internal static partial class SimdUtils } [MethodImpl(InliningOptions.ShortMethod)] - public static void InverseMmShuffle( + public static void InverseMMShuffle( byte control, - out int p3, - out int p2, - out int p1, - out int p0) + out uint p3, + out uint p2, + out uint p1, + out uint p0) { - p3 = (control >> 6) & 0x3; - p2 = (control >> 4) & 0x3; - p1 = (control >> 2) & 0x3; - p0 = (control >> 0) & 0x3; + p3 = (uint)((control >> 6) & 0x3); + p2 = (uint)((control >> 4) & 0x3); + p1 = (uint)((control >> 2) & 0x3); + p0 = (uint)((control >> 0) & 0x3); } } } diff --git a/src/ImageSharp/Compression/Zlib/Crc32.cs b/src/ImageSharp/Compression/Zlib/Crc32.cs index 39c535c773..2d0a09bd4c 100644 --- a/src/ImageSharp/Compression/Zlib/Crc32.cs +++ b/src/ImageSharp/Compression/Zlib/Crc32.cs @@ -300,7 +300,7 @@ internal static partial class Crc32 for (int i = 0; i < buffer.Length; i++) { - crc = Unsafe.Add(ref crcTableRef, (int)((crc ^ Unsafe.Add(ref bufferRef, i)) & 0xFF)) ^ (crc >> 8); + crc = Unsafe.Add(ref crcTableRef, (crc ^ Unsafe.Add(ref bufferRef, i)) & 0xFF) ^ (crc >> 8); } return crc; diff --git a/src/ImageSharp/Compression/Zlib/DeflaterEngine.cs b/src/ImageSharp/Compression/Zlib/DeflaterEngine.cs index 31fa0238bf..6009fdfbc0 100644 --- a/src/ImageSharp/Compression/Zlib/DeflaterEngine.cs +++ b/src/ImageSharp/Compression/Zlib/DeflaterEngine.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Compression.Zlib; @@ -426,8 +427,8 @@ internal sealed unsafe class DeflaterEngine : IDisposable private void SlideWindow() { Unsafe.CopyBlockUnaligned( - ref this.window.Span[0], - ref this.window.Span[DeflaterConstants.WSIZE], + ref MemoryMarshal.GetReference(this.window.Span), + ref Unsafe.Add(ref MemoryMarshal.GetReference(this.window.Span), DeflaterConstants.WSIZE), DeflaterConstants.WSIZE); this.matchStart -= DeflaterConstants.WSIZE; diff --git a/src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs b/src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs index dc11de4259..e4dc1945a8 100644 --- a/src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs +++ b/src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs @@ -206,8 +206,8 @@ internal sealed unsafe class DeflaterHuffman : IDisposable int lc = Lcode(litlen); this.literalTree.WriteSymbol(pendingBuffer, lc); - int bits = (lc - 261) / 4; - if (bits > 0 && bits <= 5) + int bits = (int)(((uint)lc - 261) / 4); + if (bits is > 0 and <= 5) { this.Pending.WriteBits(litlen & ((1 << bits) - 1), bits); } @@ -286,13 +286,13 @@ internal sealed unsafe class DeflaterHuffman : IDisposable int static_len = this.extraBits; ref byte staticLLengthRef = ref MemoryMarshal.GetReference(StaticLLength); - for (int i = 0; i < LiteralNumber; i++) + for (nuint i = 0; i < LiteralNumber; i++) { static_len += this.literalTree.Frequencies[i] * Unsafe.Add(ref staticLLengthRef, i); } ref byte staticDLengthRef = ref MemoryMarshal.GetReference(StaticDLength); - for (int i = 0; i < DistanceNumber; i++) + for (nuint i = 0; i < DistanceNumber; i++) { static_len += this.distTree.Frequencies[i] * Unsafe.Add(ref staticDLengthRef, i); } @@ -364,7 +364,7 @@ internal sealed unsafe class DeflaterHuffman : IDisposable this.literalTree.Frequencies[lc]++; if (lc >= 265 && lc < 285) { - this.extraBits += (lc - 261) / 4; + this.extraBits += (int)(((uint)lc - 261) / 4); } int dc = Dcode(distance - 1); @@ -405,10 +405,10 @@ internal sealed unsafe class DeflaterHuffman : IDisposable ref byte bit4ReverseRef = ref MemoryMarshal.GetReference(Bit4Reverse); - return (short)(Unsafe.Add(ref bit4ReverseRef, toReverse & 0xF) << 12 - | Unsafe.Add(ref bit4ReverseRef, (toReverse >> 4) & 0xF) << 8 - | Unsafe.Add(ref bit4ReverseRef, (toReverse >> 8) & 0xF) << 4 - | Unsafe.Add(ref bit4ReverseRef, toReverseRightShiftBy12)); + return (short)((Unsafe.Add(ref bit4ReverseRef, (uint)toReverse & 0xF) << 12) + | (Unsafe.Add(ref bit4ReverseRef, (uint)(toReverse >> 4) & 0xF) << 8) + | (Unsafe.Add(ref bit4ReverseRef, (uint)(toReverse >> 8) & 0xF) << 4) + | Unsafe.Add(ref bit4ReverseRef, (uint)toReverseRightShiftBy12)); } /// @@ -551,8 +551,8 @@ internal sealed unsafe class DeflaterHuffman : IDisposable int code = 0; for (int bits = 0; bits < this.maxLength; bits++) { - Unsafe.Add(ref nextCodeRef, bits) = code; - code += Unsafe.Add(ref bitLengthCountsRef, bits) << (15 - bits); + Unsafe.Add(ref nextCodeRef, (uint)bits) = code; + code += Unsafe.Add(ref bitLengthCountsRef, (uint)bits) << (15 - bits); } for (int i = 0; i < this.NumCodes; i++) @@ -560,8 +560,8 @@ internal sealed unsafe class DeflaterHuffman : IDisposable int bits = this.Length[i]; if (bits > 0) { - this.codes[i] = BitReverse(Unsafe.Add(ref nextCodeRef, bits - 1)); - Unsafe.Add(ref nextCodeRef, bits - 1) += 1 << (16 - bits); + this.codes[i] = BitReverse(Unsafe.Add(ref nextCodeRef, (uint)(bits - 1))); + Unsafe.Add(ref nextCodeRef, (uint)(bits - 1)) += 1 << (16 - bits); } } } @@ -593,13 +593,13 @@ internal sealed unsafe class DeflaterHuffman : IDisposable // Insert n into heap int pos = heapLen++; int ppos; - while (pos > 0 && this.Frequencies[Unsafe.Add(ref heapRef, ppos = (pos - 1) >> 1)] > freq) + while (pos > 0 && this.Frequencies[Unsafe.Add(ref heapRef, (uint)(ppos = (pos - 1) >> 1))] > freq) { - Unsafe.Add(ref heapRef, pos) = Unsafe.Add(ref heapRef, ppos); + Unsafe.Add(ref heapRef, pos) = Unsafe.Add(ref heapRef, (uint)ppos); pos = ppos; } - Unsafe.Add(ref heapRef, pos) = n; + Unsafe.Add(ref heapRef, (uint)pos) = n; maxCode = n; } @@ -611,7 +611,7 @@ internal sealed unsafe class DeflaterHuffman : IDisposable // this case, both literals get a 1 bit code. while (heapLen < 2) { - Unsafe.Add(ref heapRef, heapLen++) = maxCode < 2 ? ++maxCode : 0; + Unsafe.Add(ref heapRef, (uint)heapLen++) = maxCode < 2 ? ++maxCode : 0; } this.NumCodes = Math.Max(maxCode + 1, this.minNumCodes); @@ -625,14 +625,14 @@ internal sealed unsafe class DeflaterHuffman : IDisposable ref int valuesRef = ref MemoryMarshal.GetReference(valuesMemoryOwner.Memory.Span); int numNodes = numLeafs; - for (int i = 0; i < heapLen; i++) + for (nuint i = 0; i < (uint)heapLen; i++) { int node = Unsafe.Add(ref heapRef, i); - int i2 = 2 * i; + nuint i2 = 2 * i; Unsafe.Add(ref childrenRef, i2) = node; Unsafe.Add(ref childrenRef, i2 + 1) = -1; Unsafe.Add(ref valuesRef, i) = this.Frequencies[node] << 8; - Unsafe.Add(ref heapRef, i) = i; + Unsafe.Add(ref heapRef, i) = (int)i; } // Construct the Huffman tree by repeatedly combining the least two @@ -640,7 +640,7 @@ internal sealed unsafe class DeflaterHuffman : IDisposable do { int first = Unsafe.Add(ref heapRef, 0); - int last = Unsafe.Add(ref heapRef, --heapLen); + int last = Unsafe.Add(ref heapRef, (uint)--heapLen); // Propagate the hole to the leafs of the heap int ppos = 0; @@ -648,35 +648,35 @@ internal sealed unsafe class DeflaterHuffman : IDisposable while (path < heapLen) { - if (path + 1 < heapLen && Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, path)) > Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, path + 1))) + if (path + 1 < heapLen && Unsafe.Add(ref valuesRef, (uint)Unsafe.Add(ref heapRef, (uint)path)) > Unsafe.Add(ref valuesRef, (uint)Unsafe.Add(ref heapRef, (uint)(path + 1)))) { path++; } - Unsafe.Add(ref heapRef, ppos) = Unsafe.Add(ref heapRef, path); + Unsafe.Add(ref heapRef, (uint)ppos) = Unsafe.Add(ref heapRef, (uint)path); ppos = path; path = (path * 2) + 1; } // Now propagate the last element down along path. Normally // it shouldn't go too deep. - int lastVal = Unsafe.Add(ref valuesRef, last); + int lastVal = Unsafe.Add(ref valuesRef, (uint)last); while ((path = ppos) > 0 - && Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, ppos = (path - 1) >> 1)) > lastVal) + && Unsafe.Add(ref valuesRef, (uint)Unsafe.Add(ref heapRef, (uint)(ppos = (path - 1) >> 1))) > lastVal) { - Unsafe.Add(ref heapRef, path) = Unsafe.Add(ref heapRef, ppos); + Unsafe.Add(ref heapRef, (uint)path) = Unsafe.Add(ref heapRef, (uint)ppos); } - Unsafe.Add(ref heapRef, path) = last; + Unsafe.Add(ref heapRef, (uint)path) = last; int second = Unsafe.Add(ref heapRef, 0); // Create a new node father of first and second last = numNodes++; - Unsafe.Add(ref childrenRef, 2 * last) = first; - Unsafe.Add(ref childrenRef, (2 * last) + 1) = second; - int mindepth = Math.Min(Unsafe.Add(ref valuesRef, first) & 0xFF, Unsafe.Add(ref valuesRef, second) & 0xFF); - Unsafe.Add(ref valuesRef, last) = lastVal = Unsafe.Add(ref valuesRef, first) + Unsafe.Add(ref valuesRef, second) - mindepth + 1; + Unsafe.Add(ref childrenRef, (uint)(2 * last)) = first; + Unsafe.Add(ref childrenRef, (uint)((2 * last) + 1)) = second; + int mindepth = Math.Min(Unsafe.Add(ref valuesRef, (uint)first) & 0xFF, Unsafe.Add(ref valuesRef, (uint)second) & 0xFF); + Unsafe.Add(ref valuesRef, (uint)last) = lastVal = Unsafe.Add(ref valuesRef, (uint)first) + Unsafe.Add(ref valuesRef, (uint)second) - mindepth + 1; // Again, propagate the hole to the leafs ppos = 0; @@ -685,23 +685,23 @@ internal sealed unsafe class DeflaterHuffman : IDisposable while (path < heapLen) { if (path + 1 < heapLen - && Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, path)) > Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, path + 1))) + && Unsafe.Add(ref valuesRef, (uint)Unsafe.Add(ref heapRef, (uint)path)) > Unsafe.Add(ref valuesRef, (uint)Unsafe.Add(ref heapRef, (uint)(path + 1)))) { path++; } - Unsafe.Add(ref heapRef, ppos) = Unsafe.Add(ref heapRef, path); + Unsafe.Add(ref heapRef, (uint)ppos) = Unsafe.Add(ref heapRef, (uint)path); ppos = path; path = (ppos * 2) + 1; } // Now propagate the new element down along path - while ((path = ppos) > 0 && Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, ppos = (path - 1) >> 1)) > lastVal) + while ((path = ppos) > 0 && Unsafe.Add(ref valuesRef, (uint)Unsafe.Add(ref heapRef, (uint)(ppos = (path - 1) >> 1))) > lastVal) { - Unsafe.Add(ref heapRef, path) = Unsafe.Add(ref heapRef, ppos); + Unsafe.Add(ref heapRef, (uint)path) = Unsafe.Add(ref heapRef, (uint)ppos); } - Unsafe.Add(ref heapRef, path) = last; + Unsafe.Add(ref heapRef, (uint)path) = last; } while (heapLen > 1); @@ -886,21 +886,21 @@ internal sealed unsafe class DeflaterHuffman : IDisposable { if (children[(2 * i) + 1] != -1) { - int bitLength = Unsafe.Add(ref lengthsRef, i) + 1; + int bitLength = Unsafe.Add(ref lengthsRef, (uint)i) + 1; if (bitLength > maxLen) { bitLength = maxLen; overflow++; } - Unsafe.Add(ref lengthsRef, Unsafe.Add(ref childrenRef, 2 * i)) = Unsafe.Add(ref lengthsRef, Unsafe.Add(ref childrenRef, (2 * i) + 1)) = bitLength; + Unsafe.Add(ref lengthsRef, (uint)Unsafe.Add(ref childrenRef, (uint)(2 * i))) = Unsafe.Add(ref lengthsRef, (uint)Unsafe.Add(ref childrenRef, (uint)((2 * i) + 1))) = bitLength; } else { // A leaf node - int bitLength = Unsafe.Add(ref lengthsRef, i); - Unsafe.Add(ref bitLengthCountsRef, bitLength - 1)++; - lengthPtr[Unsafe.Add(ref childrenRef, 2 * i)] = (byte)Unsafe.Add(ref lengthsRef, i); + int bitLength = Unsafe.Add(ref lengthsRef, (uint)i); + Unsafe.Add(ref bitLengthCountsRef, (uint)(bitLength - 1))++; + lengthPtr[Unsafe.Add(ref childrenRef, (uint)(2 * i))] = (byte)Unsafe.Add(ref lengthsRef, (uint)i); } } } @@ -914,7 +914,7 @@ internal sealed unsafe class DeflaterHuffman : IDisposable do { // Find the first bit length which could increase: - while (Unsafe.Add(ref bitLengthCountsRef, --incrBitLen) == 0) + while (Unsafe.Add(ref bitLengthCountsRef, (uint)--incrBitLen) == 0) { } @@ -922,8 +922,8 @@ internal sealed unsafe class DeflaterHuffman : IDisposable // number of overflow nodes. do { - Unsafe.Add(ref bitLengthCountsRef, incrBitLen)--; - Unsafe.Add(ref bitLengthCountsRef, ++incrBitLen)++; + Unsafe.Add(ref bitLengthCountsRef, (uint)incrBitLen)--; + Unsafe.Add(ref bitLengthCountsRef, (uint)++incrBitLen)++; overflow -= 1 << (maxLen - 1 - incrBitLen); } while (overflow > 0 && incrBitLen < maxLen - 1); @@ -932,8 +932,8 @@ internal sealed unsafe class DeflaterHuffman : IDisposable // We may have overshot above. Move some nodes from maxLength to // maxLength-1 in that case. - Unsafe.Add(ref bitLengthCountsRef, maxLen - 1) += overflow; - Unsafe.Add(ref bitLengthCountsRef, maxLen - 2) -= overflow; + Unsafe.Add(ref bitLengthCountsRef, (uint)(maxLen - 1)) += overflow; + Unsafe.Add(ref bitLengthCountsRef, (uint)(maxLen - 2)) -= overflow; // Now recompute all bit lengths, scanning in increasing // frequency. It is simpler to reconstruct all lengths instead of @@ -945,14 +945,14 @@ internal sealed unsafe class DeflaterHuffman : IDisposable int nodeIndex = 2 * numLeafs; for (int bits = maxLen; bits != 0; bits--) { - int n = Unsafe.Add(ref bitLengthCountsRef, bits - 1); + int n = Unsafe.Add(ref bitLengthCountsRef, (uint)(bits - 1)); while (n > 0) { - int childIndex = 2 * Unsafe.Add(ref childrenRef, nodeIndex++); - if (Unsafe.Add(ref childrenRef, childIndex + 1) == -1) + int childIndex = 2 * Unsafe.Add(ref childrenRef, (uint)nodeIndex++); + if (Unsafe.Add(ref childrenRef, (uint)(childIndex + 1)) == -1) { // We found another leaf - lengthPtr[Unsafe.Add(ref childrenRef, childIndex)] = (byte)bits; + lengthPtr[Unsafe.Add(ref childrenRef, (uint)childIndex)] = (byte)bits; n--; } } diff --git a/src/ImageSharp/Diagnostics/CodeAnalysis/UnscopedRefAttribute.cs b/src/ImageSharp/Diagnostics/CodeAnalysis/UnscopedRefAttribute.cs new file mode 100644 index 0000000000..dc2c7bd196 --- /dev/null +++ b/src/ImageSharp/Diagnostics/CodeAnalysis/UnscopedRefAttribute.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. +#if NET6_0 +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +namespace System.Diagnostics.CodeAnalysis +{ + /// + /// Used to indicate a byref escapes and is not scoped. + /// + /// + /// + /// There are several cases where the C# compiler treats a as implicitly + /// - where the compiler does not allow the to escape the method. + /// + /// + /// For example: + /// + /// for instance methods. + /// parameters that refer to types. + /// parameters. + /// + /// + /// + /// This attribute is used in those instances where the should be allowed to escape. + /// + /// + /// Applying this attribute, in any form, has impact on consumers of the applicable API. It is necessary for + /// API authors to understand the lifetime implications of applying this attribute and how it may impact their users. + /// + /// + [global::System.AttributeUsage( + global::System.AttributeTargets.Method | + global::System.AttributeTargets.Property | + global::System.AttributeTargets.Parameter, + AllowMultiple = false, + Inherited = false)] + internal sealed class UnscopedRefAttribute : global::System.Attribute + { + } +} +#endif diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index cb1c7a2511..863fed359c 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -208,7 +208,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.ReadImageHeaders(stream, out _, out _); - return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metadata); + return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), new(this.infoHeader.Width, this.infoHeader.Height), this.metadata); } /// @@ -453,6 +453,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// Keeps track of rows, which have undefined pixels. private void UncompressRle4(BufferedReadStream stream, int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { + Span scratchBuffer = stackalloc byte[128]; Span cmd = stackalloc byte[2]; int count = 0; @@ -489,11 +490,11 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // If the second byte > 2, we are in 'absolute mode'. // The second byte contains the number of color indexes that follow. int max = cmd[1]; - int bytesToRead = (max + 1) / 2; + int bytesToRead = (int)(((uint)max + 1) / 2); - byte[] run = new byte[bytesToRead]; + Span run = bytesToRead <= 128 ? scratchBuffer.Slice(0, bytesToRead) : new byte[bytesToRead]; - stream.Read(run, 0, run.Length); + stream.Read(run); int idx = 0; for (int i = 0; i < max; i++) @@ -559,6 +560,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// Keeps track of rows, which have undefined pixels. private void UncompressRle8(BufferedReadStream stream, int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { + Span scratchBuffer = stackalloc byte[128]; Span cmd = stackalloc byte[2]; int count = 0; @@ -596,13 +598,13 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // Take this number of bytes from the stream as uncompressed data. int length = cmd[1]; - byte[] run = new byte[length]; + Span run = length <= 128 ? scratchBuffer.Slice(0, length) : new byte[length]; - stream.Read(run, 0, run.Length); + stream.Read(run); - run.AsSpan().CopyTo(buffer[count..]); + run.CopyTo(buffer[count..]); - count += run.Length; + count += length; // Absolute mode data is aligned to two-byte word-boundary. int padding = length & 1; @@ -639,6 +641,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// Keeps track of rows, which have undefined pixels. private void UncompressRle24(BufferedReadStream stream, int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { + Span scratchBuffer = stackalloc byte[128]; Span cmd = stackalloc byte[2]; int uncompressedPixels = 0; @@ -675,17 +678,18 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // If the second byte > 2, we are in 'absolute mode'. // Take this number of bytes from the stream as uncompressed data. int length = cmd[1]; + int length3 = length * 3; - byte[] run = new byte[length * 3]; + Span run = length3 <= 128 ? scratchBuffer.Slice(0, length3) : new byte[length3]; - stream.Read(run, 0, run.Length); + stream.Read(run); - run.AsSpan().CopyTo(buffer[(uncompressedPixels * 3)..]); + run.CopyTo(buffer[(uncompressedPixels * 3)..]); uncompressedPixels += length; // Absolute mode data is aligned to two-byte word-boundary. - int padding = run.Length & 1; + int padding = length3 & 1; stream.Skip(padding); @@ -1286,18 +1290,18 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // color masks for each color channel follow the info header. if (this.infoHeader.Compression == BmpCompression.BitFields) { - byte[] bitfieldsBuffer = new byte[12]; - stream.Read(bitfieldsBuffer, 0, 12); - Span data = bitfieldsBuffer.AsSpan(); + Span bitfieldsBuffer = stackalloc byte[12]; + stream.Read(bitfieldsBuffer); + Span data = bitfieldsBuffer; this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]); this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)); } else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS) { - byte[] bitfieldsBuffer = new byte[16]; - stream.Read(bitfieldsBuffer, 0, 16); - Span data = bitfieldsBuffer.AsSpan(); + Span bitfieldsBuffer = stackalloc byte[16]; + stream.Read(bitfieldsBuffer); + Span data = bitfieldsBuffer; this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]); this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)); @@ -1361,7 +1365,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals this.metadata.VerticalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetadata.DefaultVerticalResolution)); } - short bitsPerPixel = this.infoHeader.BitsPerPixel; + ushort bitsPerPixel = this.infoHeader.BitsPerPixel; this.bmpMetadata = this.metadata.GetBmpMetadata(); this.bmpMetadata.InfoHeaderType = infoHeaderType; this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; @@ -1470,7 +1474,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals { // Usually the color palette is 1024 byte (256 colors * 4), but the documentation does not mention a size limit. // Make sure, that we will not read pass the bitmap offset (starting position of image data). - if ((stream.Position + colorMapSizeBytes) > this.fileHeader.Offset) + if (stream.Position > this.fileHeader.Offset - colorMapSizeBytes) { BmpThrowHelper.ThrowInvalidImageContentException( $"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSizeBytes}' is invalid or the bitmap offset."); diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index f30369d19b..fd23a29e37 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -123,8 +123,8 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals BmpMetadata bmpMetadata = metadata.GetBmpMetadata(); this.bitsPerPixel ??= bmpMetadata.BitsPerPixel; - short bpp = (short)this.bitsPerPixel; - int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); + ushort bpp = (ushort)this.bitsPerPixel; + int bytesPerLine = (int)(4 * ((((uint)image.Width * bpp) + 31) / 32)); this.padding = bytesPerLine - (int)(image.Width * (bpp / 8F)); int colorPaletteSize = this.bitsPerPixel switch @@ -176,7 +176,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The metadata. /// The icc profile data. /// The bitmap information header. - private BmpInfoHeader CreateBmpInfoHeader(int width, int height, int infoHeaderSize, short bpp, int bytesPerLine, ImageMetadata metadata, byte[]? iccProfileData) + private BmpInfoHeader CreateBmpInfoHeader(int width, int height, int infoHeaderSize, ushort bpp, int bytesPerLine, ImageMetadata metadata, byte[]? iccProfileData) { int hResolution = 0; int vResolution = 0; diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs index ff02131497..c15e785730 100644 --- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs @@ -70,7 +70,7 @@ internal struct BmpInfoHeader int width, int height, short planes, - short bitsPerPixel, + ushort bitsPerPixel, BmpCompression compression = default, int imageSize = 0, int xPelsPerMeter = 0, @@ -157,7 +157,7 @@ internal struct BmpInfoHeader /// Gets or sets the number of bits per pixel, which is the color depth of the image. /// Typical values are 1, 4, 8, 16, 24 and 32. /// - public short BitsPerPixel { get; set; } + public ushort BitsPerPixel { get; set; } /// /// Gets or sets the compression method being used. @@ -311,7 +311,7 @@ internal struct BmpInfoHeader width: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(4, 2)), height: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(6, 2)), planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(8, 2)), - bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(10, 2))); + bitsPerPixel: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(10, 2))); /// /// Parses a short variant of the OS22XBITMAPHEADER. It is identical to the BITMAPCOREHEADER, except that the width and height @@ -325,7 +325,7 @@ internal struct BmpInfoHeader width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), - bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2))); + bitsPerPixel: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(14, 2))); /// /// Parses the full BMP Version 3 BITMAPINFOHEADER header (40 bytes). @@ -338,7 +338,7 @@ internal struct BmpInfoHeader width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), - bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)), + bitsPerPixel: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(14, 2)), compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)), imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)), xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)), @@ -359,7 +359,7 @@ internal struct BmpInfoHeader width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), - bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)), + bitsPerPixel: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(14, 2)), compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)), imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)), xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)), @@ -386,7 +386,7 @@ internal struct BmpInfoHeader width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), - bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2))); + bitsPerPixel: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(14, 2))); // The compression value in OS/2 bitmap has a different meaning than in windows bitmaps. // Map the OS/2 value to the windows values. @@ -431,7 +431,7 @@ internal struct BmpInfoHeader width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), - bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)), + bitsPerPixel: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(14, 2)), compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)), imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)), xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)), @@ -484,7 +484,7 @@ internal struct BmpInfoHeader BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(4, 4), this.Width); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(8, 4), this.Height); BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(12, 2), this.Planes); - BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(14, 2), this.BitsPerPixel); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(14, 2), this.BitsPerPixel); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(16, 4), (int)this.Compression); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(20, 4), this.ImageSize); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(24, 4), this.XPelsPerMeter); @@ -504,7 +504,7 @@ internal struct BmpInfoHeader BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(4, 4), this.Width); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(8, 4), this.Height); BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(12, 2), this.Planes); - BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(14, 2), this.BitsPerPixel); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(14, 2), this.BitsPerPixel); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(16, 4), (int)this.Compression); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(20, 4), this.ImageSize); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(24, 4), this.XPelsPerMeter); diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index 80c4f29336..7984126562 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -15,15 +15,25 @@ public sealed class DecoderOptions private uint maxFrames = int.MaxValue; + // Used by the FileProvider in the unit tests to set the configuration on the fly. +#pragma warning disable IDE0032 // Use auto property + private Configuration configuration = Configuration.Default; +#pragma warning restore IDE0032 // Use auto property + /// /// Gets the shared default general decoder options instance. + /// Used internally to reduce allocations for default decoding operations. /// internal static DecoderOptions Default { get; } = LazyOptions.Value; /// /// Gets a custom configuration instance to be used by the image processing pipeline. /// - public Configuration Configuration { get; internal set; } = Configuration.Default; +#pragma warning disable IDE0032 // Use auto property +#pragma warning disable RCS1085 // Use auto-implemented property. + public Configuration Configuration { get => this.configuration; init => this.configuration = value; } +#pragma warning restore RCS1085 // Use auto-implemented property. +#pragma warning restore IDE0032 // Use auto property /// /// Gets the target size to decode the image into. Scaling should use an operation equivalent to . @@ -49,4 +59,6 @@ public sealed class DecoderOptions /// Gets a value that controls how ICC profiles are handled during decode. /// public ColorProfileHandling ColorProfileHandling { get; init; } + + internal void SetConfiguration(Configuration configuration) => this.configuration = configuration; } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 3ebd65a67f..55ad2c4585 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -22,7 +22,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// /// The temp buffer used to reduce allocations. /// - private readonly byte[] buffer = new byte[16]; + private ScratchBuffer buffer; // mutable struct, don't make readonly /// /// The global color table. @@ -172,6 +172,9 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { + uint frameCount = 0; + ImageFrameMetadata? previousFrame = null; + List framesMetadata = new(); try { this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream); @@ -182,14 +185,23 @@ internal sealed class GifDecoderCore : IImageDecoderInternals { if (nextFlag == GifConstants.ImageLabel) { - this.ReadImageDescriptor(stream); + if (previousFrame != null && ++frameCount == this.maxFrames) + { + break; + } + + this.ReadFrameMetadata(stream, framesMetadata, ref previousFrame); + + // Reset per-frame state. + this.imageDescriptor = default; + this.graphicsControlExtension = default; } else if (nextFlag == GifConstants.ExtensionIntroducer) { switch (stream.ReadByte()) { case GifConstants.GraphicControlLabel: - SkipBlock(stream); // Skip graphic control extension block + this.ReadGraphicalControlExtension(stream); break; case GifConstants.CommentLabel: this.ReadComments(stream); @@ -226,9 +238,9 @@ internal sealed class GifDecoderCore : IImageDecoderInternals return new ImageInfo( new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel), - this.logicalScreenDescriptor.Width, - this.logicalScreenDescriptor.Height, - this.metadata); + new(this.logicalScreenDescriptor.Width, this.logicalScreenDescriptor.Height), + this.metadata, + framesMetadata); } /// @@ -237,13 +249,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// The containing image data. private void ReadGraphicalControlExtension(BufferedReadStream stream) { - int bytesRead = stream.Read(this.buffer, 0, 6); + int bytesRead = stream.Read(this.buffer.Span, 0, 6); if (bytesRead != 6) { GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the graphic control extension"); } - this.graphicsControlExtension = GifGraphicControlExtension.Parse(this.buffer); + this.graphicsControlExtension = GifGraphicControlExtension.Parse(this.buffer.Span); } /// @@ -252,13 +264,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// The containing image data. private void ReadImageDescriptor(BufferedReadStream stream) { - int bytesRead = stream.Read(this.buffer, 0, 9); + int bytesRead = stream.Read(this.buffer.Span, 0, 9); if (bytesRead != 9) { GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the image descriptor"); } - this.imageDescriptor = GifImageDescriptor.Parse(this.buffer); + this.imageDescriptor = GifImageDescriptor.Parse(this.buffer.Span); if (this.imageDescriptor.Height == 0 || this.imageDescriptor.Width == 0) { GifThrowHelper.ThrowInvalidImageContentException("Width or height should not be 0"); @@ -271,13 +283,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// The containing image data. private void ReadLogicalScreenDescriptor(BufferedReadStream stream) { - int bytesRead = stream.Read(this.buffer, 0, 7); + int bytesRead = stream.Read(this.buffer.Span, 0, 7); if (bytesRead != 7) { GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the logical screen descriptor"); } - this.logicalScreenDescriptor = GifLogicalScreenDescriptor.Parse(this.buffer); + this.logicalScreenDescriptor = GifLogicalScreenDescriptor.Parse(this.buffer.Span); } /// @@ -294,8 +306,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals long position = stream.Position; if (appLength == GifConstants.ApplicationBlockSize) { - stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize); - bool isXmp = this.buffer.AsSpan().StartsWith(GifConstants.XmpApplicationIdentificationBytes); + stream.Read(this.buffer.Span, 0, GifConstants.ApplicationBlockSize); + bool isXmp = this.buffer.Span.StartsWith(GifConstants.XmpApplicationIdentificationBytes); if (isXmp && !this.skipMetadata) { GifXmpApplicationExtension extension = GifXmpApplicationExtension.Read(stream, this.memoryAllocator); @@ -319,8 +331,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals // http://www.vurdalakov.net/misc/gif/netscape-buffering-application-extension if (subBlockSize == GifConstants.NetscapeLoopingSubBlockSize) { - stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize); - this.gifMetadata!.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount; + stream.Read(this.buffer.Span, 0, GifConstants.NetscapeLoopingSubBlockSize); + this.gifMetadata!.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.Span.Slice(1)).RepeatCount; stream.Skip(1); // Skip the terminator. return; } @@ -486,7 +498,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals image = new Image(this.configuration, imageWidth, imageHeight, this.metadata); } - this.SetFrameMetadata(image.Frames.RootFrame.Metadata, true); + this.SetFrameMetadata(image.Frames.RootFrame.Metadata); imageFrame = image.Frames.RootFrame; } @@ -499,7 +511,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals currentFrame = image!.Frames.CreateFrame(); - this.SetFrameMetadata(currentFrame.Metadata, false); + this.SetFrameMetadata(currentFrame.Metadata); imageFrame = currentFrame; @@ -566,8 +578,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals // #403 The left + width value can be larger than the image width for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++) { - int index = Numerics.Clamp(Unsafe.Add(ref indicesRowRef, x - descriptorLeft), 0, colorTableMaxIdx); - ref TPixel pixel = ref Unsafe.Add(ref rowRef, x); + int index = Numerics.Clamp(Unsafe.Add(ref indicesRowRef, (uint)(x - descriptorLeft)), 0, colorTableMaxIdx); + ref TPixel pixel = ref Unsafe.Add(ref rowRef, (uint)x); Rgb24 rgb = colorTable[index]; pixel.FromRgb24(rgb); } @@ -576,7 +588,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals { for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++) { - int rawIndex = Unsafe.Add(ref indicesRowRef, x - descriptorLeft); + int rawIndex = Unsafe.Add(ref indicesRowRef, (uint)(x - descriptorLeft)); // Treat any out of bounds values as transparent. if (rawIndex > colorTableMaxIdx || rawIndex == transIndex) @@ -585,7 +597,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals } int index = Numerics.Clamp(rawIndex, 0, colorTableMaxIdx); - ref TPixel pixel = ref Unsafe.Add(ref rowRef, x); + ref TPixel pixel = ref Unsafe.Add(ref rowRef, (uint)x); Rgb24 rgb = colorTable[index]; pixel.FromRgb24(rgb); } @@ -606,6 +618,37 @@ internal sealed class GifDecoderCore : IImageDecoderInternals } } + /// + /// Reads the frames metadata. + /// + /// The containing image data. + /// The collection of frame metadata. + /// The previous frame metadata. + private void ReadFrameMetadata(BufferedReadStream stream, List frameMetadata, ref ImageFrameMetadata? previousFrame) + { + this.ReadImageDescriptor(stream); + + // Skip the color table for this frame if local. + if (this.imageDescriptor.LocalColorTableFlag) + { + stream.Skip(this.imageDescriptor.LocalColorTableSize * 3); + } + + // Skip the frame indices. Pixels length + mincode size. + // The gif format does not tell us the length of the compressed data beforehand. + int minCodeSize = stream.ReadByte(); + using LzwDecoder lzwDecoder = new(this.configuration.MemoryAllocator, stream); + lzwDecoder.SkipIndices(minCodeSize, this.imageDescriptor.Width * this.imageDescriptor.Height); + + ImageFrameMetadata currentFrame = new(); + frameMetadata.Add(currentFrame); + this.SetFrameMetadata(currentFrame); + previousFrame = currentFrame; + + // Skip any remaining blocks + SkipBlock(stream); + } + /// /// Restores the current frame area to the background. /// @@ -627,18 +670,17 @@ internal sealed class GifDecoderCore : IImageDecoderInternals } /// - /// Sets the frames metadata. + /// Sets the metadata for the image frame. /// - /// The metadata. - /// Whether the metadata represents the root frame. + /// The metadata. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void SetFrameMetadata(ImageFrameMetadata meta, bool isRoot) + private void SetFrameMetadata(ImageFrameMetadata metadata) { // Frames can either use the global table or their own local table. - if (isRoot && this.logicalScreenDescriptor.GlobalColorTableFlag + if (this.logicalScreenDescriptor.GlobalColorTableFlag && this.logicalScreenDescriptor.GlobalColorTableSize > 0) { - GifFrameMetadata gifMeta = meta.GetGifMetadata(); + GifFrameMetadata gifMeta = metadata.GetGifMetadata(); gifMeta.ColorTableMode = GifColorTableMode.Global; gifMeta.ColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize; } @@ -646,7 +688,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals if (this.imageDescriptor.LocalColorTableFlag && this.imageDescriptor.LocalColorTableSize > 0) { - GifFrameMetadata gifMeta = meta.GetGifMetadata(); + GifFrameMetadata gifMeta = metadata.GetGifMetadata(); gifMeta.ColorTableMode = GifColorTableMode.Local; gifMeta.ColorTableLength = this.imageDescriptor.LocalColorTableSize; } @@ -654,7 +696,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals // Graphics control extensions is optional. if (this.graphicsControlExtension != default) { - GifFrameMetadata gifMeta = meta.GetGifMetadata(); + GifFrameMetadata gifMeta = metadata.GetGifMetadata(); gifMeta.FrameDelay = this.graphicsControlExtension.DelayTime; gifMeta.DisposalMethod = this.graphicsControlExtension.DisposalMethod; } @@ -720,4 +762,12 @@ internal sealed class GifDecoderCore : IImageDecoderInternals } } } + + private unsafe struct ScratchBuffer + { + private const int Size = 16; + private fixed byte scratch[Size]; + + public Span Span => MemoryMarshal.CreateSpan(ref this.scratch[0], Size); + } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 75c9b11860..c01cc78ef0 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -28,11 +28,6 @@ internal sealed class GifEncoderCore : IImageEncoderInternals /// private readonly Configuration configuration; - /// - /// A reusable buffer used to reduce allocations. - /// - private readonly byte[] buffer = new byte[20]; - /// /// Whether to skip metadata during encode. /// @@ -254,7 +249,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals for (int i = rgbaSpan.Length - 1; i >= 0; i--) { - if (Unsafe.Add(ref rgbaSpanRef, i).Equals(default)) + if (Unsafe.Add(ref rgbaSpanRef, (uint)i).Equals(default)) { index = i; } @@ -324,9 +319,10 @@ internal sealed class GifEncoderCore : IImageEncoderInternals backgroundColorIndex: unchecked((byte)transparencyIndex), ratio); - descriptor.WriteTo(this.buffer); + Span buffer = stackalloc byte[20]; + descriptor.WriteTo(buffer); - stream.Write(this.buffer, 0, GifLogicalScreenDescriptor.Size); + stream.Write(buffer, 0, GifLogicalScreenDescriptor.Size); } /// @@ -365,12 +361,14 @@ internal sealed class GifEncoderCore : IImageEncoderInternals return; } + Span buffer = stackalloc byte[2]; + for (int i = 0; i < metadata.Comments.Count; i++) { string comment = metadata.Comments[i]; - this.buffer[0] = GifConstants.ExtensionIntroducer; - this.buffer[1] = GifConstants.CommentLabel; - stream.Write(this.buffer, 0, 2); + buffer[1] = GifConstants.CommentLabel; + buffer[0] = GifConstants.ExtensionIntroducer; + stream.Write(buffer); // Comment will be stored in chunks of 255 bytes, if it exceeds this size. ReadOnlySpan commentSpan = comment.AsSpan(); @@ -437,22 +435,23 @@ internal sealed class GifEncoderCore : IImageEncoderInternals private void WriteExtension(TGifExtension extension, Stream stream) where TGifExtension : struct, IGifExtension { - IMemoryOwner? owner = null; - Span extensionBuffer; int extensionSize = extension.ContentLength; if (extensionSize == 0) { return; } - else if (extensionSize > this.buffer.Length - 3) + + IMemoryOwner? owner = null; + Span extensionBuffer = stackalloc byte[0]; // workaround compiler limitation + if (extensionSize > 128) { owner = this.memoryAllocator.Allocate(extensionSize + 3); extensionBuffer = owner.GetSpan(); } else { - extensionBuffer = this.buffer; + extensionBuffer = stackalloc byte[extensionSize + 3]; } extensionBuffer[0] = GifConstants.ExtensionIntroducer; @@ -489,9 +488,10 @@ internal sealed class GifEncoderCore : IImageEncoderInternals height: (ushort)image.Height, packed: packedValue); - descriptor.WriteTo(this.buffer); + Span buffer = stackalloc byte[20]; + descriptor.WriteTo(buffer); - stream.Write(this.buffer, 0, GifImageDescriptor.Size); + stream.Write(buffer, 0, GifImageDescriptor.Size); } /// diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index 1d63611fad..4d282f26c6 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -61,7 +61,7 @@ internal sealed class LzwDecoder : IDisposable } /// - /// Decodes and decompresses all pixel indices from the stream. + /// Decodes and decompresses all pixel indices from the stream, assigning the pixel values to the buffer. /// /// Minimum code size of the data. /// The pixel array to decode to. @@ -115,7 +115,7 @@ internal sealed class LzwDecoder : IDisposable for (code = 0; code < clearCode; code++) { - Unsafe.Add(ref suffixRef, code) = (byte)code; + Unsafe.Add(ref suffixRef, (uint)code) = (byte)code; } Span buffer = stackalloc byte[byte.MaxValue]; @@ -182,7 +182,7 @@ internal sealed class LzwDecoder : IDisposable if (oldCode == NullCode) { - Unsafe.Add(ref pixelStackRef, top++) = Unsafe.Add(ref suffixRef, code); + Unsafe.Add(ref pixelStackRef, (uint)top++) = Unsafe.Add(ref suffixRef, (uint)code); oldCode = code; first = code; continue; @@ -191,27 +191,181 @@ internal sealed class LzwDecoder : IDisposable int inCode = code; if (code == availableCode) { - Unsafe.Add(ref pixelStackRef, top++) = (byte)first; + Unsafe.Add(ref pixelStackRef, (uint)top++) = (byte)first; code = oldCode; } while (code > clearCode) { - Unsafe.Add(ref pixelStackRef, top++) = Unsafe.Add(ref suffixRef, code); - code = Unsafe.Add(ref prefixRef, code); + Unsafe.Add(ref pixelStackRef, (uint)top++) = Unsafe.Add(ref suffixRef, (uint)code); + code = Unsafe.Add(ref prefixRef, (uint)code); } - int suffixCode = Unsafe.Add(ref suffixRef, code); + int suffixCode = Unsafe.Add(ref suffixRef, (uint)code); first = suffixCode; - Unsafe.Add(ref pixelStackRef, top++) = suffixCode; + Unsafe.Add(ref pixelStackRef, (uint)top++) = suffixCode; // Fix for Gifs that have "deferred clear code" as per here : // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 if (availableCode < MaxStackSize) { - Unsafe.Add(ref prefixRef, availableCode) = oldCode; - Unsafe.Add(ref suffixRef, availableCode) = first; + Unsafe.Add(ref prefixRef, (uint)availableCode) = oldCode; + Unsafe.Add(ref suffixRef, (uint)availableCode) = first; + availableCode++; + if (availableCode == codeMask + 1 && availableCode < MaxStackSize) + { + codeSize++; + codeMask = (1 << codeSize) - 1; + } + } + + oldCode = inCode; + } + + // Pop a pixel off the pixel stack. + top--; + + // Clear missing pixels + xyz++; + Unsafe.Add(ref pixelsRowRef, (uint)x++) = (byte)Unsafe.Add(ref pixelStackRef, (uint)top); + } + } + + /// + /// Decodes and decompresses all pixel indices from the stream allowing skipping of the data. + /// + /// Minimum code size of the data. + /// The resulting index table length. + public void SkipIndices(int minCodeSize, int length) + { + // Calculate the clear code. The value of the clear code is 2 ^ minCodeSize + int clearCode = 1 << minCodeSize; + + // It is possible to specify a larger LZW minimum code size than the palette length in bits + // which may leave a gap in the codes where no colors are assigned. + // http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp#lzw_compression + if (minCodeSize < 2 || clearCode > MaxStackSize) + { + // Don't attempt to decode the frame indices. + // Theoretically we could determine a min code size from the length of the provided + // color palette but we won't bother since the image is most likely corrupted. + GifThrowHelper.ThrowInvalidImageContentException("Gif Image does not contain a valid LZW minimum code."); + } + + int codeSize = minCodeSize + 1; + + // Calculate the end code + int endCode = clearCode + 1; + + // Calculate the available code. + int availableCode = clearCode + 2; + + // Jillzhangs Code see: http://giflib.codeplex.com/ + // Adapted from John Cristy's ImageMagick. + int code; + int oldCode = NullCode; + int codeMask = (1 << codeSize) - 1; + int bits = 0; + + int top = 0; + int count = 0; + int bi = 0; + int xyz = 0; + + int data = 0; + int first = 0; + + ref int prefixRef = ref MemoryMarshal.GetReference(this.prefix.GetSpan()); + ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.GetSpan()); + ref int pixelStackRef = ref MemoryMarshal.GetReference(this.pixelStack.GetSpan()); + + for (code = 0; code < clearCode; code++) + { + Unsafe.Add(ref suffixRef, (uint)code) = (byte)code; + } + + Span buffer = stackalloc byte[byte.MaxValue]; + while (xyz < length) + { + if (top == 0) + { + if (bits < codeSize) + { + // Load bytes until there are enough bits for a code. + if (count == 0) + { + // Read a new data block. + count = this.ReadBlock(buffer); + if (count == 0) + { + break; + } + + bi = 0; + } + + data += buffer[bi] << bits; + + bits += 8; + bi++; + count--; + continue; + } + + // Get the next code + code = data & codeMask; + data >>= codeSize; + bits -= codeSize; + + // Interpret the code + if (code > availableCode || code == endCode) + { + break; + } + + if (code == clearCode) + { + // Reset the decoder + codeSize = minCodeSize + 1; + codeMask = (1 << codeSize) - 1; + availableCode = clearCode + 2; + oldCode = NullCode; + continue; + } + + if (oldCode == NullCode) + { + Unsafe.Add(ref pixelStackRef, (uint)top++) = Unsafe.Add(ref suffixRef, (uint)code); + oldCode = code; + first = code; + continue; + } + + int inCode = code; + if (code == availableCode) + { + Unsafe.Add(ref pixelStackRef, (uint)top++) = (byte)first; + + code = oldCode; + } + + while (code > clearCode) + { + Unsafe.Add(ref pixelStackRef, (uint)top++) = Unsafe.Add(ref suffixRef, (uint)code); + code = Unsafe.Add(ref prefixRef, (uint)code); + } + + int suffixCode = Unsafe.Add(ref suffixRef, (uint)code); + first = suffixCode; + Unsafe.Add(ref pixelStackRef, (uint)top++) = suffixCode; + + // Fix for Gifs that have "deferred clear code" as per here : + // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 + if (availableCode < MaxStackSize) + { + Unsafe.Add(ref prefixRef, (uint)availableCode) = oldCode; + Unsafe.Add(ref suffixRef, (uint)availableCode) = first; availableCode++; if (availableCode == codeMask + 1 && availableCode < MaxStackSize) { @@ -228,7 +382,6 @@ internal sealed class LzwDecoder : IDisposable // Clear missing pixels xyz++; - Unsafe.Add(ref pixelsRowRef, x++) = (byte)Unsafe.Add(ref pixelStackRef, top); } } diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs index fe27e7bf93..5253c0978a 100644 --- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs @@ -216,7 +216,7 @@ internal sealed class LzwEncoder : IDisposable [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AddCharacter(byte c, ref byte accumulatorsRef, Stream stream) { - Unsafe.Add(ref accumulatorsRef, this.accumulatorCount++) = c; + Unsafe.Add(ref accumulatorsRef, (uint)this.accumulatorCount++) = c; if (this.accumulatorCount >= 254) { this.FlushPacket(stream); @@ -278,18 +278,18 @@ internal sealed class LzwEncoder : IDisposable for (int x = offsetX; x < indexedPixels.Width; x++) { - int code = Unsafe.Add(ref rowSpanRef, x); + int code = Unsafe.Add(ref rowSpanRef, (uint)x); int freeCode = (code << MaxBits) + entry; int hashIndex = (code << HashShift) ^ entry; - if (Unsafe.Add(ref hashTableRef, hashIndex) == freeCode) + if (Unsafe.Add(ref hashTableRef, (uint)hashIndex) == freeCode) { - entry = Unsafe.Add(ref codeTableRef, hashIndex); + entry = Unsafe.Add(ref codeTableRef, (uint)hashIndex); continue; } // Non-empty slot - if (Unsafe.Add(ref hashTableRef, hashIndex) >= 0) + if (Unsafe.Add(ref hashTableRef, (uint)hashIndex) >= 0) { int disp = 1; if (hashIndex != 0) @@ -304,15 +304,15 @@ internal sealed class LzwEncoder : IDisposable hashIndex += HashSize; } - if (Unsafe.Add(ref hashTableRef, hashIndex) == freeCode) + if (Unsafe.Add(ref hashTableRef, (uint)hashIndex) == freeCode) { - entry = Unsafe.Add(ref codeTableRef, hashIndex); + entry = Unsafe.Add(ref codeTableRef, (uint)hashIndex); break; } } - while (Unsafe.Add(ref hashTableRef, hashIndex) >= 0); + while (Unsafe.Add(ref hashTableRef, (uint)hashIndex) >= 0); - if (Unsafe.Add(ref hashTableRef, hashIndex) == freeCode) + if (Unsafe.Add(ref hashTableRef, (uint)hashIndex) == freeCode) { continue; } @@ -322,8 +322,8 @@ internal sealed class LzwEncoder : IDisposable entry = code; if (this.freeEntry < MaxMaxCode) { - Unsafe.Add(ref codeTableRef, hashIndex) = this.freeEntry++; // code -> hashtable - Unsafe.Add(ref hashTableRef, hashIndex) = freeCode; + Unsafe.Add(ref codeTableRef, (uint)hashIndex) = this.freeEntry++; // code -> hashtable + Unsafe.Add(ref hashTableRef, (uint)hashIndex) = freeCode; } else { diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.tt b/src/ImageSharp/Formats/ImageExtensions.Save.tt index 7498aa7c3b..64f3bde9cc 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.tt +++ b/src/ImageSharp/Formats/ImageExtensions.Save.tt @@ -77,7 +77,7 @@ public static partial class ImageExtensions public static void SaveAs<#= fmt #>(this Image source, string path, <#= fmt #>Encoder encoder) => source.Save( path, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance)); + encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(<#= fmt #>Format.Instance)); /// /// Saves the image to the given stream with the <#= fmt #> format. @@ -91,7 +91,7 @@ public static partial class ImageExtensions public static Task SaveAs<#= fmt #>Async(this Image source, string path, <#= fmt #>Encoder encoder, CancellationToken cancellationToken = default) => source.SaveAsync( path, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance), + encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(<#= fmt #>Format.Instance), cancellationToken); /// @@ -124,7 +124,7 @@ public static partial class ImageExtensions public static void SaveAs<#= fmt #>(this Image source, Stream stream, <#= fmt #>Encoder encoder) => source.Save( stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance)); + encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(<#= fmt #>Format.Instance)); /// /// Saves the image to the given stream with the <#= fmt #> format. @@ -138,7 +138,7 @@ public static partial class ImageExtensions public static Task SaveAs<#= fmt #>Async(this Image source, Stream stream, <#= fmt #>Encoder encoder, CancellationToken cancellationToken = default) => source.SaveAsync( stream, - encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(<#= fmt #>Format.Instance), + encoder ?? source.GetConfiguration().ImageFormatsManager.GetEncoder(<#= fmt #>Format.Instance), cancellationToken); <# diff --git a/src/ImageSharp/Formats/ImageFormatManager.cs b/src/ImageSharp/Formats/ImageFormatManager.cs index 8b4e320c5a..0ee4bc79b9 100644 --- a/src/ImageSharp/Formats/ImageFormatManager.cs +++ b/src/ImageSharp/Formats/ImageFormatManager.cs @@ -102,9 +102,7 @@ public class ImageFormatManager /// if a match is found; otherwise, public bool TryFindFormatByFileExtension(string extension, [NotNullWhen(true)] out IImageFormat? format) { - Guard.NotNullOrWhiteSpace(extension, nameof(extension)); - - if (extension[0] == '.') + if (!string.IsNullOrWhiteSpace(extension) && extension[0] == '.') { extension = extension[1..]; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs index 325d6c26f5..01d112bd6f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs @@ -15,25 +15,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components; /// 8x8 matrix of coefficients. /// // ReSharper disable once InconsistentNaming -[StructLayout(LayoutKind.Explicit)] -internal unsafe partial struct Block8x8 +[StructLayout(LayoutKind.Explicit, Size = 2 * Size)] +internal partial struct Block8x8 { /// /// A number of scalar coefficients in a /// public const int Size = 64; -#pragma warning disable IDE0051 // Remove unused private member - /// - /// A placeholder buffer so the actual struct occupies exactly 64 * 2 bytes. - /// - /// - /// This is not used directly in the code. - /// - [FieldOffset(0)] - private fixed short data[Size]; -#pragma warning restore IDE0051 - /// /// Gets or sets a value at the given index /// @@ -47,7 +36,7 @@ internal unsafe partial struct Block8x8 DebugGuard.MustBeBetweenOrEqualTo(idx, 0, Size - 1, nameof(idx)); ref short selfRef = ref Unsafe.As(ref this); - return Unsafe.Add(ref selfRef, idx); + return Unsafe.Add(ref selfRef, (uint)idx); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -56,7 +45,7 @@ internal unsafe partial struct Block8x8 DebugGuard.MustBeBetweenOrEqualTo(idx, 0, Size - 1, nameof(idx)); ref short selfRef = ref Unsafe.As(ref this); - Unsafe.Add(ref selfRef, idx) = value; + Unsafe.Add(ref selfRef, (uint)idx) = value; } } @@ -74,9 +63,10 @@ internal unsafe partial struct Block8x8 public static Block8x8 Load(Span data) { - Unsafe.SkipInit(out Block8x8 result); - result.LoadFrom(data); - return result; + DebugGuard.MustBeGreaterThanOrEqualTo(data.Length, Size, "data is too small"); + + ref byte src = ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + return Unsafe.ReadUnaligned(ref src); } /// @@ -104,9 +94,10 @@ internal unsafe partial struct Block8x8 /// public void CopyTo(Span destination) { - ref byte selfRef = ref Unsafe.As(ref this); - ref byte destRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(destination)); - Unsafe.CopyBlockUnaligned(ref destRef, ref selfRef, Size * sizeof(short)); + DebugGuard.MustBeGreaterThanOrEqualTo(destination.Length, Size, "destination is too small"); + + ref byte destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + Unsafe.WriteUnaligned(ref destRef, this); } /// @@ -135,19 +126,6 @@ internal unsafe partial struct Block8x8 } } - /// - /// Load raw 16bit integers from source. - /// - /// Source - [MethodImpl(InliningOptions.ShortMethod)] - public void LoadFrom(Span source) - { - ref byte sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref byte destRef = ref Unsafe.As(ref this); - - Unsafe.CopyBlockUnaligned(ref destRef, ref sourceRef, Size * sizeof(short)); - } - /// /// Cast and copy -s from the beginning of 'source' span. /// @@ -207,12 +185,12 @@ internal unsafe partial struct Block8x8 // Given mask is not actually suitable for lzcnt as 1's represent zero elements and 0's represent non-zero elements // So we need to invert it - int lzcnt = BitOperations.LeadingZeroCount(~(uint)areEqual); + uint lzcnt = (uint)BitOperations.LeadingZeroCount(~(uint)areEqual); // As input number is represented by 2 bits in the mask, we need to divide lzcnt result by 2 // to get the exact number of zero elements in the stride - int strideRelativeIndex = 15 - (lzcnt / 2); - return (i * 16) + strideRelativeIndex; + uint strideRelativeIndex = 15 - (lzcnt / 2); + return (i * 16) + (nint)strideRelativeIndex; } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs index 2610befcfe..93bb7be36e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs @@ -16,7 +16,7 @@ internal partial struct Block8x8F { var CMin4 = new Vector4(0F); var CMax4 = new Vector4(maximum); - var COff4 = new Vector4(MathF.Ceiling(maximum / 2)); + var COff4 = new Vector4(MathF.Ceiling(maximum * 0.5F)); this.V0L = Numerics.Clamp(this.V0L + COff4, CMin4, CMax4); this.V0R = Numerics.Clamp(this.V0R + COff4, CMin4, CMax4); @@ -42,33 +42,33 @@ internal partial struct Block8x8F [MethodImpl(InliningOptions.ShortMethod)] public void NormalizeColorsAndRoundInPlaceVector8(float maximum) { - var off = new Vector(MathF.Ceiling(maximum / 2)); + var off = new Vector(MathF.Ceiling(maximum * 0.5F)); var max = new Vector(maximum); - + ref Vector row0 = ref Unsafe.As>(ref this.V0L); row0 = NormalizeAndRound(row0, off, max); - + ref Vector row1 = ref Unsafe.As>(ref this.V1L); row1 = NormalizeAndRound(row1, off, max); - + ref Vector row2 = ref Unsafe.As>(ref this.V2L); row2 = NormalizeAndRound(row2, off, max); - + ref Vector row3 = ref Unsafe.As>(ref this.V3L); row3 = NormalizeAndRound(row3, off, max); - + ref Vector row4 = ref Unsafe.As>(ref this.V4L); row4 = NormalizeAndRound(row4, off, max); - + ref Vector row5 = ref Unsafe.As>(ref this.V5L); row5 = NormalizeAndRound(row5, off, max); - + ref Vector row6 = ref Unsafe.As>(ref this.V6L); row6 = NormalizeAndRound(row6, off, max); - + ref Vector row7 = ref Unsafe.As>(ref this.V7L); row7 = NormalizeAndRound(row7, off, max); - + } /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt index aa211ea22b..19b795c23a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt @@ -29,7 +29,7 @@ internal partial struct Block8x8F { var CMin4 = new Vector4(0F); var CMax4 = new Vector4(maximum); - var COff4 = new Vector4(MathF.Ceiling(maximum / 2)); + var COff4 = new Vector4(MathF.Ceiling(maximum * 0.5F)); <# @@ -53,7 +53,7 @@ internal partial struct Block8x8F [MethodImpl(InliningOptions.ShortMethod)] public void NormalizeColorsAndRoundInPlaceVector8(float maximum) { - var off = new Vector(MathF.Ceiling(maximum / 2)); + var off = new Vector(MathF.Ceiling(maximum * 0.5F)); var max = new Vector(maximum); <# diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs index 78c82f11a0..63be76f00f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs @@ -41,9 +41,9 @@ internal partial struct Block8x8F ref Vector256 bBase = ref b.V0; ref Vector256 destRef = ref dest.V01; - var multiplyIntoInt16ShuffleMask = Vector256.Create(0, 1, 4, 5, 2, 3, 6, 7); + Vector256 multiplyIntoInt16ShuffleMask = Vector256.Create(0, 1, 4, 5, 2, 3, 6, 7); - for (nint i = 0; i < 8; i += 2) + for (nuint i = 0; i < 8; i += 2) { Vector256 row0 = Avx.ConvertToVector256Int32(Avx.Multiply(Unsafe.Add(ref aBase, i + 0), Unsafe.Add(ref bBase, i + 0))); Vector256 row1 = Avx.ConvertToVector256Int32(Avx.Multiply(Unsafe.Add(ref aBase, i + 1), Unsafe.Add(ref bBase, i + 1))); @@ -51,7 +51,7 @@ internal partial struct Block8x8F Vector256 row = Avx2.PackSignedSaturate(row0, row1); row = Avx2.PermuteVar8x32(row.AsInt32(), multiplyIntoInt16ShuffleMask).AsInt16(); - Unsafe.Add(ref destRef, (IntPtr)((uint)i / 2)) = row; + Unsafe.Add(ref destRef, i / 2) = row; } } @@ -64,13 +64,13 @@ internal partial struct Block8x8F ref Vector128 destBase = ref Unsafe.As>(ref dest); - for (int i = 0; i < 16; i += 2) + for (nuint i = 0; i < 16; i += 2) { Vector128 left = Sse2.ConvertToVector128Int32(Sse.Multiply(Unsafe.Add(ref aBase, i + 0), Unsafe.Add(ref bBase, i + 0))); Vector128 right = Sse2.ConvertToVector128Int32(Sse.Multiply(Unsafe.Add(ref aBase, i + 1), Unsafe.Add(ref bBase, i + 1))); Vector128 row = Sse2.PackSignedSaturate(left, right); - Unsafe.Add(ref destBase, (IntPtr)((uint)i / 2)) = row; + Unsafe.Add(ref destBase, i / 2) = row; } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs index 813d9c37b7..efc1dbd729 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs @@ -30,13 +30,13 @@ internal partial struct Block8x8F } // TODO: Optimize: implement all cases with scale-specific, loopless code! - this.CopyArbitraryScale(ref areaOrigin, areaStride, horizontalScale, verticalScale); + this.CopyArbitraryScale(ref areaOrigin, (uint)areaStride, (uint)horizontalScale, (uint)verticalScale); } private void CopyTo2x2Scale(ref float areaOrigin, int areaStride) { ref Vector2 destBase = ref Unsafe.As(ref areaOrigin); - int destStride = (int)((uint)areaStride / 2); + nuint destStride = (uint)areaStride / 2; WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 0, destStride); WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 1, destStride); @@ -48,12 +48,12 @@ internal partial struct Block8x8F WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 7, destStride); [MethodImpl(MethodImplOptions.AggressiveInlining)] - static void WidenCopyRowImpl2x2(ref Vector4 selfBase, ref Vector2 destBase, nint row, nint destStride) + static void WidenCopyRowImpl2x2(ref Vector4 selfBase, ref Vector2 destBase, nuint row, nuint destStride) { ref Vector4 sLeft = ref Unsafe.Add(ref selfBase, 2 * row); ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); - nint offset = 2 * row * destStride; + nuint offset = 2 * row * destStride; ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset)); ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride)); @@ -86,23 +86,23 @@ internal partial struct Block8x8F } [MethodImpl(InliningOptions.ColdPath)] - private void CopyArbitraryScale(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) + private void CopyArbitraryScale(ref float areaOrigin, uint areaStride, uint horizontalScale, uint verticalScale) { - for (int y = 0; y < 8; y++) + for (nuint y = 0; y < 8; y++) { - int yy = y * verticalScale; - int y8 = y * 8; + nuint yy = y * verticalScale; + nuint y8 = y * 8; - for (int x = 0; x < 8; x++) + for (nuint x = 0; x < 8; x++) { - int xx = x * horizontalScale; + nuint xx = x * horizontalScale; - float value = this[y8 + x]; - nint baseIdx = (yy * areaStride) + xx; + float value = this[(int)(y8 + x)]; + nuint baseIdx = (yy * areaStride) + xx; - for (nint i = 0; i < verticalScale; i++, baseIdx += areaStride) + for (nuint i = 0; i < verticalScale; i++, baseIdx += areaStride) { - for (nint j = 0; j < horizontalScale; j++) + for (nuint j = 0; j < horizontalScale; j++) { // area[xx + j, yy + i] = value; Unsafe.Add(ref areaOrigin, baseIdx + j) = value; @@ -128,8 +128,8 @@ internal partial struct Block8x8F [MethodImpl(MethodImplOptions.AggressiveInlining)] static void CopyRowImpl(ref byte origin, ref byte dest, int destStride, int row) { - origin = ref Unsafe.Add(ref origin, row * 8 * sizeof(float)); - dest = ref Unsafe.Add(ref dest, row * destStride); + origin = ref Unsafe.Add(ref origin, (uint)row * 8 * sizeof(float)); + dest = ref Unsafe.Add(ref dest, (uint)(row * destStride)); Unsafe.CopyBlock(ref dest, ref origin, 8 * sizeof(float)); } } @@ -150,8 +150,8 @@ internal partial struct Block8x8F [MethodImpl(MethodImplOptions.AggressiveInlining)] static void CopyRowImpl(ref byte origin, ref byte dest, int sourceStride, int row) { - origin = ref Unsafe.Add(ref origin, row * sourceStride); - dest = ref Unsafe.Add(ref dest, row * 8 * sizeof(float)); + origin = ref Unsafe.Add(ref origin, (uint)(row * sourceStride)); + dest = ref Unsafe.Add(ref dest, (uint)row * 8 * sizeof(float)); Unsafe.CopyBlock(ref dest, ref origin, 8 * sizeof(float)); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index 3bd5d28e74..d432e82d24 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -71,48 +71,47 @@ internal partial struct Block8x8F : IEquatable /// The index /// The float value at the specified index public float this[int idx] + { + get => this[(uint)idx]; + set => this[(uint)idx] = value; + } + + internal float this[nuint idx] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - DebugGuard.MustBeBetweenOrEqualTo(idx, 0, Size - 1, nameof(idx)); + DebugGuard.MustBeBetweenOrEqualTo((int)idx, 0, Size - 1, nameof(idx)); ref float selfRef = ref Unsafe.As(ref this); - return Unsafe.Add(ref selfRef, (nint)(uint)idx); + return Unsafe.Add(ref selfRef, idx); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - DebugGuard.MustBeBetweenOrEqualTo(idx, 0, Size - 1, nameof(idx)); + DebugGuard.MustBeBetweenOrEqualTo((int)idx, 0, Size - 1, nameof(idx)); ref float selfRef = ref Unsafe.As(ref this); - Unsafe.Add(ref selfRef, (nint)(uint)idx) = value; + Unsafe.Add(ref selfRef, idx) = value; } } public float this[int x, int y] { - get => this[(y * 8) + x]; - set => this[(y * 8) + x] = value; - } - - public static Block8x8F Load(Span data) - { - Block8x8F result = default; - result.LoadFrom(data); - return result; + get => this[((uint)y * 8) + (uint)x]; + set => this[((uint)y * 8) + (uint)x] = value; } /// /// Load raw 32bit floating point data from source. /// - /// Source + /// Source [MethodImpl(InliningOptions.ShortMethod)] - public void LoadFrom(Span source) + public static Block8x8F Load(Span data) { - ref byte s = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref byte d = ref Unsafe.As(ref this); + DebugGuard.MustBeGreaterThanOrEqualTo(data.Length, Size, "data is too small"); - Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float)); + ref byte src = ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + return Unsafe.ReadUnaligned(ref src); } /// @@ -138,10 +137,10 @@ internal partial struct Block8x8F : IEquatable [MethodImpl(InliningOptions.ShortMethod)] public unsafe void ScaledCopyTo(float[] dest) { - fixed (void* ptr = &this.V0L) - { - Marshal.Copy((IntPtr)ptr, dest, 0, Size); - } + DebugGuard.MustBeGreaterThanOrEqualTo(dest.Length, Size, "dest is too small"); + + ref byte destRef = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(dest)); + Unsafe.WriteUnaligned(ref destRef, this); } public float[] ToArray() @@ -425,7 +424,7 @@ internal partial struct Block8x8F : IEquatable Vector256 targetVector = Vector256.Create(value); ref Vector256 blockStride = ref this.V0; - for (int i = 0; i < RowCount; i++) + for (nuint i = 0; i < RowCount; i++) { Vector256 areEqual = Avx2.CompareEqual(Avx.ConvertToVector256Int32WithTruncation(Unsafe.Add(ref this.V0, i)), targetVector); if (Avx2.MoveMask(areEqual.AsByte()) != equalityMask) @@ -439,7 +438,7 @@ internal partial struct Block8x8F : IEquatable ref float scalars = ref Unsafe.As(ref this); - for (int i = 0; i < Size; i++) + for (nuint i = 0; i < Size; i++) { if ((int)Unsafe.Add(ref scalars, i) != value) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykArm64.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykArm64.cs new file mode 100644 index 0000000000..11122d3b89 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykArm64.cs @@ -0,0 +1,95 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class CmykArm64 : JpegColorConverterArm64 + { + public CmykArm64(int precision) + : base(JpegColorSpace.Cmyk, precision) + { + } + + /// + public override void ConvertToRgbInplace(in ComponentValues values) + { + ref Vector128 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 c1Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 c2Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector128 c3Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + + // Used for the color conversion + var scale = Vector128.Create(1 / (this.MaximumValue * this.MaximumValue)); + + nint n = (nint)(uint)values.Component0.Length / Vector128.Count; + for (nint i = 0; i < n; i++) + { + ref Vector128 c = ref Unsafe.Add(ref c0Base, i); + ref Vector128 m = ref Unsafe.Add(ref c1Base, i); + ref Vector128 y = ref Unsafe.Add(ref c2Base, i); + Vector128 k = Unsafe.Add(ref c3Base, i); + + k = AdvSimd.Multiply(k, scale); + c = AdvSimd.Multiply(c, k); + m = AdvSimd.Multiply(m, k); + y = AdvSimd.Multiply(y, k); + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + => ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); + + public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span rLane, Span gLane, Span bLane) + { + ref Vector128 destC = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 destM = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 destY = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector128 destK = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + + ref Vector128 srcR = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector128 srcG = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector128 srcB = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + + var scale = Vector128.Create(maxValue); + + nint n = (nint)(uint)values.Component0.Length / Vector128.Count; + for (nint i = 0; i < n; i++) + { + Vector128 ctmp = AdvSimd.Subtract(scale, Unsafe.Add(ref srcR, i)); + Vector128 mtmp = AdvSimd.Subtract(scale, Unsafe.Add(ref srcG, i)); + Vector128 ytmp = AdvSimd.Subtract(scale, Unsafe.Add(ref srcB, i)); + Vector128 ktmp = AdvSimd.Min(ctmp, AdvSimd.Min(mtmp, ytmp)); + + Vector128 kMask = AdvSimd.Not(AdvSimd.CompareEqual(ktmp, scale)); + + ctmp = AdvSimd.And(AdvSimd.Arm64.Divide(AdvSimd.Subtract(ctmp, ktmp), AdvSimd.Subtract(scale, ktmp)), kMask); + mtmp = AdvSimd.And(AdvSimd.Arm64.Divide(AdvSimd.Subtract(mtmp, ktmp), AdvSimd.Subtract(scale, ktmp)), kMask); + ytmp = AdvSimd.And(AdvSimd.Arm64.Divide(AdvSimd.Subtract(ytmp, ktmp), AdvSimd.Subtract(scale, ktmp)), kMask); + + Unsafe.Add(ref destC, i) = AdvSimd.Subtract(scale, AdvSimd.Multiply(ctmp, scale)); + Unsafe.Add(ref destM, i) = AdvSimd.Subtract(scale, AdvSimd.Multiply(mtmp, scale)); + Unsafe.Add(ref destY, i) = AdvSimd.Subtract(scale, AdvSimd.Multiply(ytmp, scale)); + Unsafe.Add(ref destK, i) = AdvSimd.Subtract(scale, ktmp); + } + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs index 7d7b7e1859..76d188f457 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs @@ -32,8 +32,8 @@ internal abstract partial class JpegColorConverterBase // Used for the color conversion var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); - nint n = values.Component0.Length / Vector256.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) { ref Vector256 c = ref Unsafe.Add(ref c0Base, i); ref Vector256 m = ref Unsafe.Add(ref c1Base, i); @@ -71,8 +71,8 @@ internal abstract partial class JpegColorConverterBase var scale = Vector256.Create(maxValue); - nint n = values.Component0.Length / Vector256.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) { Vector256 ctmp = Avx.Subtract(scale, Unsafe.Add(ref srcR, i)); Vector256 mtmp = Avx.Subtract(scale, Unsafe.Add(ref srcG, i)); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs index 93dcd378c8..a59be009b7 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs @@ -30,8 +30,8 @@ internal abstract partial class JpegColorConverterBase var scale = new Vector(1 / (this.MaximumValue * this.MaximumValue)); - nint n = values.Component0.Length / Vector.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.VectorCount(); + for (nuint i = 0; i < n; i++) { ref Vector c = ref Unsafe.Add(ref cBase, i); ref Vector m = ref Unsafe.Add(ref mBase, i); @@ -78,8 +78,8 @@ internal abstract partial class JpegColorConverterBase // Used for the color conversion var scale = new Vector(maxValue); - nint n = values.Component0.Length / Vector.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.VectorCount(); + for (nuint i = 0; i < n; i++) { Vector ctmp = scale - Unsafe.Add(ref srcR, i); Vector mtmp = scale - Unsafe.Add(ref srcG, i); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs new file mode 100644 index 0000000000..6bb8fd7aa3 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleArm.cs @@ -0,0 +1,68 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using static SixLabors.ImageSharp.SimdUtils; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class GrayscaleArm : JpegColorConverterArm + { + public GrayscaleArm(int precision) + : base(JpegColorSpace.Grayscale, precision) + { + } + + /// + public override void ConvertToRgbInplace(in ComponentValues values) + { + ref Vector128 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + + // Used for the color conversion + var scale = Vector128.Create(1 / this.MaximumValue); + + nuint n = values.Component0.Vector128Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector128 c0 = ref Unsafe.Add(ref c0Base, i); + c0 = AdvSimd.Multiply(c0, scale); + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + ref Vector128 destLuminance = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + + ref Vector128 srcRed = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector128 srcGreen = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector128 srcBlue = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + + // Used for the color conversion + var f0299 = Vector128.Create(0.299f); + var f0587 = Vector128.Create(0.587f); + var f0114 = Vector128.Create(0.114f); + + nuint n = values.Component0.Vector128Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector128 r = ref Unsafe.Add(ref srcRed, i); + ref Vector128 g = ref Unsafe.Add(ref srcGreen, i); + ref Vector128 b = ref Unsafe.Add(ref srcBlue, i); + + // luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b) + Unsafe.Add(ref destLuminance, i) = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f0114, b), f0587, g), f0299, r); + } + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs index 9cdbe71e84..a9e1c5d130 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs @@ -27,8 +27,8 @@ internal abstract partial class JpegColorConverterBase // Used for the color conversion var scale = Vector256.Create(1 / this.MaximumValue); - nint n = values.Component0.Length / Vector256.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) { ref Vector256 c0 = ref Unsafe.Add(ref c0Base, i); c0 = Avx.Multiply(c0, scale); @@ -53,8 +53,8 @@ internal abstract partial class JpegColorConverterBase var f0587 = Vector256.Create(0.587f); var f0114 = Vector256.Create(0.114f); - nint n = values.Component0.Length / Vector256.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) { ref Vector256 r = ref Unsafe.Add(ref srcRed, i); ref Vector256 g = ref Unsafe.Add(ref srcGreen, i); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs index 16dd7c768a..4d3b5c60bd 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs @@ -28,7 +28,7 @@ internal abstract partial class JpegColorConverterBase ref float valuesRef = ref MemoryMarshal.GetReference(values); float scale = 1 / maxValue; - for (nint i = 0; i < values.Length; i++) + for (nuint i = 0; i < (uint)values.Length; i++) { Unsafe.Add(ref valuesRef, i) *= scale; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs index 4d2355b95d..cac10636f5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs @@ -24,8 +24,8 @@ internal abstract partial class JpegColorConverterBase var scale = new Vector(1 / this.MaximumValue); - nint n = values.Component0.Length / Vector.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.VectorCount(); + for (nuint i = 0; i < n; i++) { ref Vector c0 = ref Unsafe.Add(ref cBase, i); c0 *= scale; @@ -53,8 +53,8 @@ internal abstract partial class JpegColorConverterBase var gMult = new Vector(0.587f); var bMult = new Vector(0.114f); - nint n = values.Component0.Length / Vector.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.VectorCount(); + for (nuint i = 0; i < n; i++) { Vector r = Unsafe.Add(ref srcR, i); Vector g = Unsafe.Add(ref srcR, i); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs new file mode 100644 index 0000000000..75eeb17dd3 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbArm.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class RgbArm : JpegColorConverterArm + { + public RgbArm(int precision) + : base(JpegColorSpace.RGB, precision) + { + } + + /// + public override void ConvertToRgbInplace(in ComponentValues values) + { + ref Vector128 rBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 gBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 bBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + // Used for the color conversion + var scale = Vector128.Create(1 / this.MaximumValue); + nuint n = values.Component0.Vector128Count(); + for (nuint i = 0; i < n; i++) + { + ref Vector128 r = ref Unsafe.Add(ref rBase, i); + ref Vector128 g = ref Unsafe.Add(ref gBase, i); + ref Vector128 b = ref Unsafe.Add(ref bBase, i); + r = AdvSimd.Multiply(r, scale); + g = AdvSimd.Multiply(g, scale); + b = AdvSimd.Multiply(b, scale); + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + rLane.CopyTo(values.Component0); + gLane.CopyTo(values.Component1); + bLane.CopyTo(values.Component2); + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs index b6c5117d44..b56728fa71 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs @@ -29,8 +29,8 @@ internal abstract partial class JpegColorConverterBase // Used for the color conversion var scale = Vector256.Create(1 / this.MaximumValue); - nint n = values.Component0.Length / Vector256.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) { ref Vector256 r = ref Unsafe.Add(ref rBase, i); ref Vector256 g = ref Unsafe.Add(ref gBase, i); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs index e51b0df4d2..bd3142fa13 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs @@ -28,8 +28,8 @@ internal abstract partial class JpegColorConverterBase var scale = new Vector(1 / this.MaximumValue); - nint n = values.Component0.Length / Vector.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.VectorCount(); + for (nuint i = 0; i < n; i++) { ref Vector r = ref Unsafe.Add(ref rBase, i); ref Vector g = ref Unsafe.Add(ref gBase, i); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrArm.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrArm.cs new file mode 100644 index 0000000000..4f7cf1ed65 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrArm.cs @@ -0,0 +1,122 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.X86; +using static SixLabors.ImageSharp.SimdUtils; + +// ReSharper disable ImpureMethodCallOnReadonlyValueField +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class YCbCrArm : JpegColorConverterArm + { + public YCbCrArm(int precision) + : base(JpegColorSpace.YCbCr, precision) + { + } + + /// + public override void ConvertToRgbInplace(in ComponentValues values) + { + ref Vector128 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 c1Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 c2Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + // Used for the color conversion + var chromaOffset = Vector128.Create(-this.HalfValue); + var scale = Vector128.Create(1 / this.MaximumValue); + var rCrMult = Vector128.Create(YCbCrScalar.RCrMult); + var gCbMult = Vector128.Create(-YCbCrScalar.GCbMult); + var gCrMult = Vector128.Create(-YCbCrScalar.GCrMult); + var bCbMult = Vector128.Create(YCbCrScalar.BCbMult); + + // Walking 8 elements at one step: + nuint n = (uint)values.Component0.Length / (uint)Vector128.Count; + for (nuint i = 0; i < n; i++) + { + // y = yVals[i]; + // cb = cbVals[i] - 128F; + // cr = crVals[i] - 128F; + ref Vector128 c0 = ref Unsafe.Add(ref c0Base, i); + ref Vector128 c1 = ref Unsafe.Add(ref c1Base, i); + ref Vector128 c2 = ref Unsafe.Add(ref c2Base, i); + + Vector128 y = c0; + Vector128 cb = AdvSimd.Add(c1, chromaOffset); + Vector128 cr = AdvSimd.Add(c2, chromaOffset); + + // r = y + (1.402F * cr); + // g = y - (0.344136F * cb) - (0.714136F * cr); + // b = y + (1.772F * cb); + Vector128 r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); + Vector128 g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); + Vector128 b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); + + r = AdvSimd.Multiply(AdvSimd.RoundToNearest(r), scale); + g = AdvSimd.Multiply(AdvSimd.RoundToNearest(g), scale); + b = AdvSimd.Multiply(AdvSimd.RoundToNearest(b), scale); + + c0 = r; + c1 = g; + c2 = b; + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + ref Vector128 destY = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 destCb = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 destCr = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + ref Vector128 srcR = + ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane)); + ref Vector128 srcG = + ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane)); + ref Vector128 srcB = + ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane)); + + // Used for the color conversion + var chromaOffset = Vector128.Create(this.HalfValue); + + var f0299 = Vector128.Create(0.299f); + var f0587 = Vector128.Create(0.587f); + var f0114 = Vector128.Create(0.114f); + var fn0168736 = Vector128.Create(-0.168736f); + var fn0331264 = Vector128.Create(-0.331264f); + var fn0418688 = Vector128.Create(-0.418688f); + var fn0081312F = Vector128.Create(-0.081312F); + var f05 = Vector128.Create(0.5f); + + nuint n = (uint)values.Component0.Length / (uint)Vector128.Count; + for (nuint i = 0; i < n; i++) + { + Vector128 r = Unsafe.Add(ref srcR, i); + Vector128 g = Unsafe.Add(ref srcG, i); + Vector128 b = Unsafe.Add(ref srcB, i); + + // y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b) + // cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b) + // cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b) + Vector128 y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f0114, b), f0587, g), f0299, r); + Vector128 cb = AdvSimd.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f05, b), fn0331264, g), fn0168736, r)); + Vector128 cr = AdvSimd.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(fn0081312F, b), fn0418688, g), f05, r)); + + Unsafe.Add(ref destY, i) = y; + Unsafe.Add(ref destCb, i) = cb; + Unsafe.Add(ref destCr, i) = cr; + } + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs index 081b985dbb..c5fa786e2c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs @@ -38,8 +38,8 @@ internal abstract partial class JpegColorConverterBase var bCbMult = Vector256.Create(YCbCrScalar.BCbMult); // Walking 8 elements at one step: - nint n = values.Component0.Length / Vector256.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) { // y = yVals[i]; // cb = cbVals[i] - 128F; @@ -98,8 +98,8 @@ internal abstract partial class JpegColorConverterBase var fn0081312F = Vector256.Create(-0.081312F); var f05 = Vector256.Create(0.5f); - nint n = values.Component0.Length / Vector256.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) { Vector256 r = Unsafe.Add(ref srcR, i); Vector256 g = Unsafe.Add(ref srcG, i); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs index 85211d4abf..a5d0c889e3 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrVector.cs @@ -35,8 +35,8 @@ internal abstract partial class JpegColorConverterBase var gCrMult = new Vector(-YCbCrScalar.GCrMult); var bCbMult = new Vector(YCbCrScalar.BCbMult); - nint n = values.Component0.Length / Vector.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.VectorCount(); + for (nuint i = 0; i < n; i++) { // y = yVals[i]; // cb = cbVals[i] - 128F; @@ -103,8 +103,8 @@ internal abstract partial class JpegColorConverterBase var gCrMult = new Vector(0.418688f); var bCrMult = new Vector(0.081312f); - nint n = values.Component0.Length / Vector.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.VectorCount(); + for (nuint i = 0; i < n; i++) { Vector r = Unsafe.Add(ref srcR, i); Vector g = Unsafe.Add(ref srcG, i); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKArm64.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKArm64.cs new file mode 100644 index 0000000000..285ba62cfd --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKArm64.cs @@ -0,0 +1,133 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.X86; +using static SixLabors.ImageSharp.SimdUtils; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + internal sealed class YccKArm64 : JpegColorConverterArm64 + { + public YccKArm64(int precision) + : base(JpegColorSpace.Ycck, precision) + { + } + + /// + public override void ConvertToRgbInplace(in ComponentValues values) + { + ref Vector128 c0Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 c1Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 c2Base = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + ref Vector128 kBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); + + // Used for the color conversion + var chromaOffset = Vector128.Create(-this.HalfValue); + var scale = Vector128.Create(1 / (this.MaximumValue * this.MaximumValue)); + var max = Vector128.Create(this.MaximumValue); + var rCrMult = Vector128.Create(YCbCrScalar.RCrMult); + var gCbMult = Vector128.Create(-YCbCrScalar.GCbMult); + var gCrMult = Vector128.Create(-YCbCrScalar.GCrMult); + var bCbMult = Vector128.Create(YCbCrScalar.BCbMult); + + // Walking 8 elements at one step: + nuint n = (uint)values.Component0.Length / (uint)Vector128.Count; + for (nuint i = 0; i < n; i++) + { + // y = yVals[i]; + // cb = cbVals[i] - 128F; + // cr = crVals[i] - 128F; + // k = kVals[i] / 256F; + ref Vector128 c0 = ref Unsafe.Add(ref c0Base, i); + ref Vector128 c1 = ref Unsafe.Add(ref c1Base, i); + ref Vector128 c2 = ref Unsafe.Add(ref c2Base, i); + Vector128 y = c0; + Vector128 cb = AdvSimd.Add(c1, chromaOffset); + Vector128 cr = AdvSimd.Add(c2, chromaOffset); + Vector128 scaledK = AdvSimd.Multiply(Unsafe.Add(ref kBase, i), scale); + + // r = y + (1.402F * cr); + // g = y - (0.344136F * cb) - (0.714136F * cr); + // b = y + (1.772F * cb); + Vector128 r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); + Vector128 g = + HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); + Vector128 b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); + + r = AdvSimd.Subtract(max, AdvSimd.RoundToNearest(r)); + g = AdvSimd.Subtract(max, AdvSimd.RoundToNearest(g)); + b = AdvSimd.Subtract(max, AdvSimd.RoundToNearest(b)); + + r = AdvSimd.Multiply(r, scaledK); + g = AdvSimd.Multiply(g, scaledK); + b = AdvSimd.Multiply(b, scaledK); + + c0 = r; + c1 = g; + c2 = b; + } + } + + /// + public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane) + { + // rgb -> cmyk + CmykArm64.ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); + + // cmyk -> ycck + ref Vector128 destY = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); + ref Vector128 destCb = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); + ref Vector128 destCr = + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); + + ref Vector128 srcR = ref destY; + ref Vector128 srcG = ref destCb; + ref Vector128 srcB = ref destCr; + + // Used for the color conversion + var maxSampleValue = Vector128.Create(this.MaximumValue); + + var chromaOffset = Vector128.Create(this.HalfValue); + + var f0299 = Vector128.Create(0.299f); + var f0587 = Vector128.Create(0.587f); + var f0114 = Vector128.Create(0.114f); + var fn0168736 = Vector128.Create(-0.168736f); + var fn0331264 = Vector128.Create(-0.331264f); + var fn0418688 = Vector128.Create(-0.418688f); + var fn0081312F = Vector128.Create(-0.081312F); + var f05 = Vector128.Create(0.5f); + + nuint n = (uint)values.Component0.Length / (uint)Vector128.Count; + for (nuint i = 0; i < n; i++) + { + Vector128 r = AdvSimd.Subtract(maxSampleValue, Unsafe.Add(ref srcR, i)); + Vector128 g = AdvSimd.Subtract(maxSampleValue, Unsafe.Add(ref srcG, i)); + Vector128 b = AdvSimd.Subtract(maxSampleValue, Unsafe.Add(ref srcB, i)); + + // y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b) + // cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b) + // cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b) + Vector128 y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f0114, b), f0587, g), f0299, r); + Vector128 cb = AdvSimd.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(f05, b), fn0331264, g), fn0168736, r)); + Vector128 cr = AdvSimd.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(AdvSimd.Multiply(fn0081312F, b), fn0418688, g), f05, r)); + + Unsafe.Add(ref destY, i) = y; + Unsafe.Add(ref destCb, i) = cb; + Unsafe.Add(ref destCr, i) = cr; + } + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs index 1f79cbffb6..efe40ba085 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKAvx.cs @@ -40,8 +40,8 @@ internal abstract partial class JpegColorConverterBase var bCbMult = Vector256.Create(YCbCrScalar.BCbMult); // Walking 8 elements at one step: - nint n = values.Component0.Length / Vector256.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) { // y = yVals[i]; // cb = cbVals[i] - 128F; @@ -109,8 +109,8 @@ internal abstract partial class JpegColorConverterBase var fn0081312F = Vector256.Create(-0.081312F); var f05 = Vector256.Create(0.5f); - nint n = values.Component0.Length / Vector256.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.Vector256Count(); + for (nuint i = 0; i < n; i++) { Vector256 r = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcR, i)); Vector256 g = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcG, i)); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs index 91a6cedc06..570a401adf 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YccKVector.cs @@ -36,8 +36,8 @@ internal abstract partial class JpegColorConverterBase var gCrMult = new Vector(-YCbCrScalar.GCrMult); var bCbMult = new Vector(YCbCrScalar.BCbMult); - nint n = values.Component0.Length / Vector.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.VectorCount(); + for (nuint i = 0; i < n; i++) { // y = yVals[i]; // cb = cbVals[i] - 128F; @@ -107,8 +107,8 @@ internal abstract partial class JpegColorConverterBase var gCrMult = new Vector(0.418688f); var bCrMult = new Vector(0.081312f); - nint n = values.Component0.Length / Vector.Count; - for (nint i = 0; i < n; i++) + nuint n = values.Component0.VectorCount(); + for (nuint i = 0; i < n; i++) { Vector r = maxSampleValue - Unsafe.Add(ref srcR, i); Vector g = maxSampleValue - Unsafe.Add(ref srcG, i); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs new file mode 100644 index 0000000000..bfbbf200c8 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + /// + /// abstract base for implementations + /// based on instructions. + /// + /// + /// Converters of this family would expect input buffers lengths to be + /// divisible by 8 without a remainder. + /// This is guaranteed by real-life data as jpeg stores pixels via 8x8 blocks. + /// DO NOT pass test data of invalid size to these converters as they + /// potentially won't do a bound check and return a false positive result. + /// + internal abstract class JpegColorConverterArm : JpegColorConverterBase + { + protected JpegColorConverterArm(JpegColorSpace colorSpace, int precision) + : base(colorSpace, precision) + { + } + + public static bool IsSupported => AdvSimd.IsSupported; + + public sealed override bool IsAvailable => IsSupported; + + public sealed override int ElementsPerBatch => Vector128.Count; + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm64.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm64.cs new file mode 100644 index 0000000000..d6d4d6ef93 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterArm64.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components; + +internal abstract partial class JpegColorConverterBase +{ + /// + /// abstract base for implementations + /// based on instructions. + /// + /// + /// Converters of this family would expect input buffers lengths to be + /// divisible by 8 without a remainder. + /// This is guaranteed by real-life data as jpeg stores pixels via 8x8 blocks. + /// DO NOT pass test data of invalid size to these converters as they + /// potentially won't do a bound check and return a false positive result. + /// + internal abstract class JpegColorConverterArm64 : JpegColorConverterBase + { + protected JpegColorConverterArm64(JpegColorSpace colorSpace, int precision) + : base(colorSpace, precision) + { + } + + public static bool IsSupported => AdvSimd.Arm64.IsSupported; + + public sealed override bool IsAvailable => IsSupported; + + public sealed override int ElementsPerBatch => Vector128.Count; + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs index 62f48af16e..041f6b0578 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs @@ -26,7 +26,7 @@ internal abstract partial class JpegColorConverterBase this.ColorSpace = colorSpace; this.Precision = precision; this.MaximumValue = MathF.Pow(2, precision) - 1; - this.HalfValue = MathF.Ceiling(this.MaximumValue / 2); + this.HalfValue = MathF.Ceiling(this.MaximumValue * 0.5F); // /2 } /// @@ -138,6 +138,11 @@ internal abstract partial class JpegColorConverterBase return new YCbCrAvx(precision); } + if (JpegColorConverterArm.IsSupported) + { + return new YCbCrArm(precision); + } + if (JpegColorConverterVector.IsSupported) { return new YCbCrVector(precision); @@ -157,6 +162,11 @@ internal abstract partial class JpegColorConverterBase return new YccKAvx(precision); } + if (JpegColorConverterArm64.IsSupported) + { + return new YccKArm64(precision); + } + if (JpegColorConverterVector.IsSupported) { return new YccKVector(precision); @@ -176,6 +186,11 @@ internal abstract partial class JpegColorConverterBase return new CmykAvx(precision); } + if (JpegColorConverterArm64.IsSupported) + { + return new CmykArm64(precision); + } + if (JpegColorConverterVector.IsSupported) { return new CmykVector(precision); @@ -195,6 +210,11 @@ internal abstract partial class JpegColorConverterBase return new GrayscaleAvx(precision); } + if (JpegColorConverterArm.IsSupported) + { + return new GrayscaleArm(precision); + } + if (JpegColorConverterVector.IsSupported) { return new GrayScaleVector(precision); @@ -214,12 +234,17 @@ internal abstract partial class JpegColorConverterBase return new RgbAvx(precision); } + if (JpegColorConverterArm.IsSupported) + { + return new RgbArm(precision); + } + if (JpegColorConverterVector.IsSupported) { - return new RgbScalar(precision); + return new RgbVector(precision); } - return new GrayscaleScalar(precision); + return new RgbScalar(precision); } /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs index c9ee55cd77..cf2369b2cb 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs @@ -62,7 +62,7 @@ internal readonly struct AdobeMarker : IEquatable /// /// The byte array containing metadata to parse. /// The marker to return. - public static bool TryParse(byte[] bytes, out AdobeMarker marker) + public static bool TryParse(ReadOnlySpan bytes, out AdobeMarker marker) { if (ProfileResolver.IsProfile(bytes, ProfileResolver.AdobeMarker)) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticScanDecoder.cs index 922d7093b6..02a346ff07 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticScanDecoder.cs @@ -53,6 +53,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder private ArithmeticDecodingTable[] acDecodingTables; + // Don't make this a ReadOnlySpan, as the values need to get updated. private readonly byte[] fixedBin = { 113, 0, 0, 0 }; private readonly CancellationToken cancellationToken; @@ -231,7 +232,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder } } - private ref byte GetFixedBinReference() => ref this.fixedBin[0]; + private ref byte GetFixedBinReference() => ref MemoryMarshal.GetArrayDataReference(this.fixedBin); /// /// Decodes the entropy coded data. @@ -470,7 +471,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder this.DecodeBlockBaseline( component, - ref Unsafe.Add(ref blockRef, (nint)(uint)blockCol), + ref Unsafe.Add(ref blockRef, (uint)blockCol), ref acDecodingTable, ref dcDecodingTable); } @@ -521,7 +522,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder this.DecodeBlockBaseline( component, - ref Unsafe.Add(ref blockRef, (nint)(uint)k), + ref Unsafe.Add(ref blockRef, (uint)k), ref acDecodingTable, ref dcDecodingTable); @@ -560,7 +561,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder this.DecodeBlockBaseline( component, - ref Unsafe.Add(ref blockRef, (nint)(uint)i), + ref Unsafe.Add(ref blockRef, (uint)i), ref acDecodingTable, ref dcDecodingTable); @@ -611,7 +612,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder this.DecodeBlockProgressiveDc( component, - ref Unsafe.Add(ref blockRef, (nint)(uint)blockCol), + ref Unsafe.Add(ref blockRef, (uint)blockCol), ref dcDecodingTable); } } @@ -653,7 +654,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder this.DecodeBlockProgressiveDc( component, - ref Unsafe.Add(ref blockRef, (nint)(uint)i), + ref Unsafe.Add(ref blockRef, (uint)i), ref dcDecodingTable); this.HandleRestart(); @@ -680,7 +681,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder this.DecodeBlockProgressiveAc( component, - ref Unsafe.Add(ref blockRef, (nint)(uint)i), + ref Unsafe.Add(ref blockRef, (uint)i), ref acDecodingTable); this.HandleRestart(); @@ -705,7 +706,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder // Sections F.2.4.1 & F.1.4.4.1: Decoding of DC coefficients. // Table F.4: Point to statistics bin S0 for DC coefficient coding. - ref byte st = ref Unsafe.Add(ref component.DcStatistics.GetReference(), component.DcContext); + ref byte st = ref Unsafe.Add(ref component.DcStatistics.GetReference(), (uint)component.DcContext); // Figure F.19: Decode_DC_DIFF if (this.DecodeBinaryDecision(ref reader, ref st) == 0) @@ -717,7 +718,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder // Figure F.21: Decoding nonzero value v. // Figure F.22: Decoding the sign of v. int sign = this.DecodeBinaryDecision(ref reader, ref Unsafe.Add(ref st, 1)); - st = ref Unsafe.Add(ref st, (nint)(uint)(2 + sign)); + st = ref Unsafe.Add(ref st, (uint)(2 + sign)); // Figure F.23: Decoding the magnitude category of v. int m = this.DecodeBinaryDecision(ref reader, ref st); @@ -761,7 +762,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder } } - v += 1; + v++; if (sign != 0) { v = -v; @@ -856,7 +857,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder } } - v += 1; + v++; if (sign != 0) { v = -v; @@ -955,7 +956,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder // Sections F.2.4.1 & F.1.4.4.1: Decoding of DC coefficients. // Table F.4: Point to statistics bin S0 for DC coefficient coding. - ref byte st = ref Unsafe.Add(ref component.DcStatistics.GetReference(), component.DcContext); + ref byte st = ref Unsafe.Add(ref component.DcStatistics.GetReference(), (uint)component.DcContext); /* Figure F.19: Decode_DC_DIFF */ if (this.DecodeBinaryDecision(ref reader, ref st) == 0) @@ -967,7 +968,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder // Figure F.21: Decoding nonzero value v // Figure F.22: Decoding the sign of v int sign = this.DecodeBinaryDecision(ref reader, ref Unsafe.Add(ref st, 1)); - st = ref Unsafe.Add(ref st, (nint)(uint)(2 + sign)); + st = ref Unsafe.Add(ref st, (uint)(2 + sign)); // Figure F.23: Decoding the magnitude category of v. int m = this.DecodeBinaryDecision(ref reader, ref st); @@ -1012,7 +1013,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder } } - v += 1; + v++; if (sign != 0) { v = -v; @@ -1082,7 +1083,7 @@ internal class ArithmeticScanDecoder : IJpegScanDecoder } } - v += 1; + v++; if (sign != 0) { v = -v; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticStatistics.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticStatistics.cs index 1e890d8269..9bd4110d07 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticStatistics.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticStatistics.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; internal class ArithmeticStatistics @@ -18,7 +20,7 @@ internal class ArithmeticStatistics public int Identifier { get; private set; } - public ref byte GetReference() => ref this.statistics[0]; + public ref byte GetReference() => ref MemoryMarshal.GetArrayDataReference(this.statistics); public ref byte GetReference(int offset) => ref this.statistics[offset]; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs index 51d8d03593..300a773311 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs @@ -25,7 +25,7 @@ internal sealed class DownScalingComponentProcessor2 : ComponentProcessor Buffer2D spectralBuffer = this.Component.SpectralBlocks; float maximumValue = this.Frame.MaxColorChannelValue; - float normalizationValue = MathF.Ceiling(maximumValue / 2); + float normalizationValue = MathF.Ceiling(maximumValue * 0.5F); int destAreaStride = this.ColorBuffer.Width; @@ -67,30 +67,30 @@ internal sealed class DownScalingComponentProcessor2 : ComponentProcessor public static void ScaledCopyTo(ref Block8x8F block, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale) { // TODO: Optimize: implement all cases with scale-specific, loopless code! - CopyArbitraryScale(ref block, ref destRef, destStrideWidth, horizontalScale, verticalScale); + CopyArbitraryScale(ref block, ref destRef, (uint)destStrideWidth, (uint)horizontalScale, (uint)verticalScale); [MethodImpl(InliningOptions.ColdPath)] - static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) + static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, uint areaStride, uint horizontalScale, uint verticalScale) { - for (int y = 0; y < 4; y++) + for (nuint y = 0; y < 4; y++) { - int yy = y * verticalScale; - int y8 = y * 8; + nuint yy = y * verticalScale; + nuint y8 = y * 8; - for (int x = 0; x < 4; x++) + for (nuint x = 0; x < 4; x++) { - int xx = x * horizontalScale; + nuint xx = x * horizontalScale; float value = block[y8 + x]; - for (int i = 0; i < verticalScale; i++) + for (nuint i = 0; i < verticalScale; i++) { - int baseIdx = ((yy + i) * areaStride) + xx; + nuint baseIdx = ((yy + i) * areaStride) + xx; - for (int j = 0; j < horizontalScale; j++) + for (nuint j = 0; j < horizontalScale; j++) { // area[xx + j, yy + i] = value; - Unsafe.Add(ref areaOrigin, (nint)(uint)(baseIdx + j)) = value; + Unsafe.Add(ref areaOrigin, baseIdx + j) = value; } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs index b8a40f53b1..7984169902 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs @@ -25,7 +25,7 @@ internal sealed class DownScalingComponentProcessor4 : ComponentProcessor Buffer2D spectralBuffer = this.Component.SpectralBlocks; float maximumValue = this.Frame.MaxColorChannelValue; - float normalizationValue = MathF.Ceiling(maximumValue / 2); + float normalizationValue = MathF.Ceiling(maximumValue * 0.5F); int destAreaStride = this.ColorBuffer.Width; @@ -67,30 +67,30 @@ internal sealed class DownScalingComponentProcessor4 : ComponentProcessor public static void ScaledCopyTo(ref Block8x8F block, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale) { // TODO: Optimize: implement all cases with scale-specific, loopless code! - CopyArbitraryScale(ref block, ref destRef, destStrideWidth, horizontalScale, verticalScale); + CopyArbitraryScale(ref block, ref destRef, (uint)destStrideWidth, (uint)horizontalScale, (uint)verticalScale); [MethodImpl(InliningOptions.ColdPath)] - static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) + static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, uint areaStride, uint horizontalScale, uint verticalScale) { - for (int y = 0; y < 2; y++) + for (nuint y = 0; y < 2; y++) { - int yy = y * verticalScale; - int y8 = y * 8; + nuint yy = y * verticalScale; + nuint y8 = y * 8; - for (int x = 0; x < 2; x++) + for (nuint x = 0; x < 2; x++) { - int xx = x * horizontalScale; + nuint xx = x * horizontalScale; float value = block[y8 + x]; - for (int i = 0; i < verticalScale; i++) + for (nuint i = 0; i < verticalScale; i++) { - int baseIdx = ((yy + i) * areaStride) + xx; + nuint baseIdx = ((yy + i) * areaStride) + xx; - for (int j = 0; j < horizontalScale; j++) + for (nuint j = 0; j < horizontalScale; j++) { // area[xx + j, yy + i] = value; - Unsafe.Add(ref areaOrigin, (nint)(uint)(baseIdx + j)) = value; + Unsafe.Add(ref areaOrigin, baseIdx + j) = value; } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs index 121b745465..f3b09e6b49 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs @@ -22,7 +22,7 @@ internal sealed class DownScalingComponentProcessor8 : ComponentProcessor Buffer2D spectralBuffer = this.Component.SpectralBlocks; float maximumValue = this.Frame.MaxColorChannelValue; - float normalizationValue = MathF.Ceiling(maximumValue / 2); + float normalizationValue = MathF.Ceiling(maximumValue * 0.5F); int destAreaStride = this.ColorBuffer.Width; @@ -67,20 +67,20 @@ internal sealed class DownScalingComponentProcessor8 : ComponentProcessor { destRef = value; Unsafe.Add(ref destRef, 1) = value; - Unsafe.Add(ref destRef, 0 + (nint)(uint)destStrideWidth) = value; - Unsafe.Add(ref destRef, 1 + (nint)(uint)destStrideWidth) = value; + Unsafe.Add(ref destRef, 0 + (uint)destStrideWidth) = value; + Unsafe.Add(ref destRef, 1 + (uint)destStrideWidth) = value; return; } // TODO: Optimize: implement all cases with scale-specific, loopless code! - for (int y = 0; y < verticalScale; y++) + for (nuint y = 0; y < (uint)verticalScale; y++) { - for (int x = 0; x < horizontalScale; x++) + for (nuint x = 0; x < (uint)horizontalScale; x++) { - Unsafe.Add(ref destRef, (nint)(uint)x) = value; + Unsafe.Add(ref destRef, x) = value; } - destRef = ref Unsafe.Add(ref destRef, (nint)(uint)destStrideWidth); + destRef = ref Unsafe.Add(ref destRef, (uint)destStrideWidth); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs index e5c5098d05..bbd2bff53b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs @@ -211,7 +211,7 @@ internal class HuffmanScanDecoder : IJpegScanDecoder this.DecodeBlockBaseline( component, - ref Unsafe.Add(ref blockRef, blockCol), + ref Unsafe.Add(ref blockRef, (uint)blockCol), ref dcHuffmanTable, ref acHuffmanTable); } @@ -255,7 +255,7 @@ internal class HuffmanScanDecoder : IJpegScanDecoder this.DecodeBlockBaseline( component, - ref Unsafe.Add(ref blockRef, i), + ref Unsafe.Add(ref blockRef, (uint)i), ref dcHuffmanTable, ref acHuffmanTable); @@ -297,7 +297,7 @@ internal class HuffmanScanDecoder : IJpegScanDecoder this.DecodeBlockBaseline( component, - ref Unsafe.Add(ref blockRef, k), + ref Unsafe.Add(ref blockRef, (uint)k), ref dcHuffmanTable, ref acHuffmanTable); @@ -417,7 +417,7 @@ internal class HuffmanScanDecoder : IJpegScanDecoder this.DecodeBlockProgressiveDC( component, - ref Unsafe.Add(ref blockRef, blockCol), + ref Unsafe.Add(ref blockRef, (uint)blockCol), ref dcHuffmanTable); } } @@ -459,7 +459,7 @@ internal class HuffmanScanDecoder : IJpegScanDecoder this.DecodeBlockProgressiveDC( component, - ref Unsafe.Add(ref blockRef, i), + ref Unsafe.Add(ref blockRef, (uint)i), ref dcHuffmanTable); this.HandleRestart(); @@ -485,7 +485,7 @@ internal class HuffmanScanDecoder : IJpegScanDecoder } this.DecodeBlockProgressiveAC( - ref Unsafe.Add(ref blockRef, i), + ref Unsafe.Add(ref blockRef, (uint)i), ref acHuffmanTable); this.HandleRestart(); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs index e13b26f9a9..153dc8a03e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs @@ -69,7 +69,7 @@ internal readonly struct JFifMarker : IEquatable /// /// The byte array containing metadata to parse. /// The marker to return. - public static bool TryParse(byte[] bytes, out JFifMarker marker) + public static bool TryParse(ReadOnlySpan bytes, out JFifMarker marker) { if (ProfileResolver.IsProfile(bytes, ProfileResolver.JFifMarker)) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/ComponentProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/ComponentProcessor.cs index 2bc1405509..be27385cd0 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/ComponentProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/ComponentProcessor.cs @@ -122,8 +122,8 @@ internal class ComponentProcessor : IDisposable ref Vector256 sourceVectorRef = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); // Spans are guaranteed to be multiple of 8 so no extra 'remainder' steps are needed - nint count = source.Length / Vector256.Count; - for (nint i = 0; i < count; i++) + nuint count = source.Vector256Count(); + for (nuint i = 0; i < count; i++) { Unsafe.Add(ref targetVectorRef, i) = Avx.Add(Unsafe.Add(ref targetVectorRef, i), Unsafe.Add(ref sourceVectorRef, i)); } @@ -133,15 +133,15 @@ internal class ComponentProcessor : IDisposable ref Vector targetVectorRef = ref Unsafe.As>(ref MemoryMarshal.GetReference(target)); ref Vector sourceVectorRef = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - nint count = source.Length / Vector.Count; - for (nint i = 0; i < count; i++) + nuint count = source.VectorCount(); + for (nuint i = 0; i < count; i++) { Unsafe.Add(ref targetVectorRef, i) += Unsafe.Add(ref sourceVectorRef, i); } ref float targetRef = ref MemoryMarshal.GetReference(target); ref float sourceRef = ref MemoryMarshal.GetReference(source); - for (nint i = count * Vector.Count; i < source.Length; i++) + for (nuint i = count * (uint)Vector.Count; i < (uint)source.Length; i++) { Unsafe.Add(ref targetRef, i) += Unsafe.Add(ref sourceRef, i); } @@ -157,7 +157,7 @@ internal class ComponentProcessor : IDisposable // Ideally we need to use log2: Numerics.Log2((uint)factor) // but division by 2 works just fine in this case - int haddIterationsCount = (int)((uint)factor / 2); + uint haddIterationsCount = (uint)factor / 2; // Transform spans so that it only contains 'remainder' // values for the scalar fallback code @@ -166,9 +166,9 @@ internal class ComponentProcessor : IDisposable source = source.Slice(touchedCount); target = target.Slice(touchedCount / factor); - uint length = (uint)touchedCount / (uint)Vector256.Count; + nuint length = Numerics.Vector256Count(touchedCount); - for (int i = 0; i < haddIterationsCount; i++) + for (uint i = 0; i < haddIterationsCount; i++) { length /= 2; @@ -200,9 +200,9 @@ internal class ComponentProcessor : IDisposable ref Vector256 targetVectorRef = ref Unsafe.As>(ref MemoryMarshal.GetReference(target)); // Spans are guaranteed to be multiple of 8 so no extra 'remainder' steps are needed - nint count = target.Length / Vector256.Count; + nuint count = target.Vector256Count(); var multiplierVector = Vector256.Create(multiplier); - for (nint i = 0; i < count; i++) + for (nuint i = 0; i < count; i++) { Unsafe.Add(ref targetVectorRef, i) = Avx.Multiply(Unsafe.Add(ref targetVectorRef, i), multiplierVector); } @@ -211,15 +211,15 @@ internal class ComponentProcessor : IDisposable { ref Vector targetVectorRef = ref Unsafe.As>(ref MemoryMarshal.GetReference(target)); - nint count = target.Length / Vector.Count; + nuint count = target.VectorCount(); var multiplierVector = new Vector(multiplier); - for (nint i = 0; i < count; i++) + for (nuint i = 0; i < count; i++) { Unsafe.Add(ref targetVectorRef, i) *= multiplierVector; } ref float targetRef = ref MemoryMarshal.GetReference(target); - for (nint i = count * Vector.Count; i < target.Length; i++) + for (nuint i = count * (uint)Vector.Count; i < (uint)target.Length; i++) { Unsafe.Add(ref targetRef, i) *= multiplier; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs index 8edbc3c407..d74494f9e5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs @@ -123,7 +123,7 @@ internal class HuffmanScanEncoder private bool IsStreamFlushNeeded { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.emitWriteIndex < (uint)this.emitBuffer.Length / 2; + get => this.emitWriteIndex < (int)((uint)this.emitBuffer.Length / 2); } public void BuildHuffmanTable(JpegHuffmanTableConfig tableConfig) @@ -180,7 +180,7 @@ internal class HuffmanScanEncoder Span blockSpan = component.SpectralBlocks.DangerousGetRowSpan(y: 0); ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); - for (nint k = 0; k < w; k++) + for (nuint k = 0; k < (uint)w; k++) { this.WriteBlock( component, @@ -219,7 +219,7 @@ internal class HuffmanScanEncoder Span blockSpan = component.SpectralBlocks.DangerousGetRowSpan(y: i); ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); - for (nint k = 0; k < w; k++) + for (nuint k = 0; k < (uint)w; k++) { this.WriteBlock( component, @@ -246,9 +246,9 @@ internal class HuffmanScanEncoder private void EncodeScanBaselineInterleaved(JpegFrame frame, SpectralConverter converter, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - nint mcu = 0; - nint mcusPerColumn = frame.McusPerColumn; - nint mcusPerLine = frame.McusPerLine; + int mcu = 0; + int mcusPerColumn = frame.McusPerColumn; + int mcusPerLine = frame.McusPerLine; for (int j = 0; j < mcusPerColumn; j++) { @@ -258,21 +258,21 @@ internal class HuffmanScanEncoder converter.ConvertStrideBaseline(); // Encode spectral to binary - for (nint i = 0; i < mcusPerLine; i++) + for (int i = 0; i < mcusPerLine; i++) { // Scan an interleaved mcu... process components in order - nint mcuCol = mcu % mcusPerLine; - for (nint k = 0; k < frame.Components.Length; k++) + int mcuCol = mcu % mcusPerLine; + for (int k = 0; k < frame.Components.Length; k++) { Component component = frame.Components[k]; ref HuffmanLut dcHuffmanTable = ref this.dcHuffmanTables[component.DcTableId]; ref HuffmanLut acHuffmanTable = ref this.acHuffmanTables[component.AcTableId]; - nint h = component.HorizontalSamplingFactor; + int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; - nint blockColBase = mcuCol * h; + nuint blockColBase = (uint)(mcuCol * h); // Scan out an mcu's worth of this component; that's just determined // by the basic H and V specified for the component @@ -281,9 +281,9 @@ internal class HuffmanScanEncoder Span blockSpan = component.SpectralBlocks.DangerousGetRowSpan(y); ref Block8x8 blockRef = ref MemoryMarshal.GetReference(blockSpan); - for (nint x = 0; x < h; x++) + for (nuint x = 0; x < (uint)h; x++) { - nint blockCol = blockColBase + x; + nuint blockCol = blockColBase + x; this.WriteBlock( component, @@ -315,8 +315,8 @@ internal class HuffmanScanEncoder private void EncodeThreeComponentBaselineInterleavedScanNoSubsampling(JpegFrame frame, SpectralConverter converter, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - nint mcusPerColumn = frame.McusPerColumn; - nint mcusPerLine = frame.McusPerLine; + nuint mcusPerColumn = (uint)frame.McusPerColumn; + nuint mcusPerLine = (uint)frame.McusPerLine; Component c2 = frame.Components[2]; Component c1 = frame.Components[1]; @@ -333,7 +333,7 @@ internal class HuffmanScanEncoder ref Block8x8 c1BlockRef = ref MemoryMarshal.GetReference(c1.SpectralBlocks.DangerousGetRowSpan(y: 0)); ref Block8x8 c2BlockRef = ref MemoryMarshal.GetReference(c2.SpectralBlocks.DangerousGetRowSpan(y: 0)); - for (nint j = 0; j < mcusPerColumn; j++) + for (nuint j = 0; j < mcusPerColumn; j++) { cancellationToken.ThrowIfCancellationRequested(); @@ -341,7 +341,7 @@ internal class HuffmanScanEncoder converter.ConvertStrideBaseline(); // Encode spectral to binary - for (nint i = 0; i < mcusPerLine; i++) + for (nuint i = 0; i < mcusPerLine; i++) { this.WriteBlock( c0, diff --git a/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs index cae89fc3cf..7e102f696d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.Intrinsic.cs @@ -99,7 +99,7 @@ internal static partial class FloatingPointDCT var mm256_F_1_4142 = Vector256.Create(1.414213562f); Vector256 tmp13 = Avx.Add(tmp1, tmp3); - Vector256 tmp12 = SimdUtils.HwIntrinsics.MultiplySubstract(tmp13, Avx.Subtract(tmp1, tmp3), mm256_F_1_4142); + Vector256 tmp12 = SimdUtils.HwIntrinsics.MultiplySubtract(tmp13, Avx.Subtract(tmp1, tmp3), mm256_F_1_4142); tmp0 = Avx.Add(tmp10, tmp13); tmp3 = Avx.Subtract(tmp10, tmp13); diff --git a/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.cs b/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.cs index 15348d4474..0aca33b4c9 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/FloatingPointDCT.cs @@ -69,7 +69,7 @@ internal static partial class FloatingPointDCT { ref float tableRef = ref Unsafe.As(ref quantTable); ref float multipliersRef = ref MemoryMarshal.GetReference(AdjustmentCoefficients); - for (nint i = 0; i < Block8x8F.Size; i++) + for (nuint i = 0; i < Block8x8F.Size; i++) { ref float elemRef = ref Unsafe.Add(ref tableRef, i); elemRef = 0.125f * elemRef * Unsafe.Add(ref multipliersRef, i); @@ -88,7 +88,7 @@ internal static partial class FloatingPointDCT { ref float tableRef = ref Unsafe.As(ref quantTable); ref float multipliersRef = ref MemoryMarshal.GetReference(AdjustmentCoefficients); - for (nint i = 0; i < Block8x8F.Size; i++) + for (nuint i = 0; i < Block8x8F.Size; i++) { ref float elemRef = ref Unsafe.Add(ref tableRef, i); elemRef = 0.125f / (elemRef * Unsafe.Add(ref multipliersRef, i)); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ScaledFloatingPointDCT.cs b/src/ImageSharp/Formats/Jpeg/Components/ScaledFloatingPointDCT.cs index 369626a96b..98e3857973 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ScaledFloatingPointDCT.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ScaledFloatingPointDCT.cs @@ -40,7 +40,7 @@ internal static class ScaledFloatingPointDCT public static void AdjustToIDCT(ref Block8x8F quantTable) { ref float tableRef = ref Unsafe.As(ref quantTable); - for (nint i = 0; i < Block8x8F.Size; i++) + for (nuint i = 0; i < Block8x8F.Size; i++) { ref float elemRef = ref Unsafe.Add(ref tableRef, i); elemRef = 0.125f * elemRef; @@ -103,10 +103,10 @@ internal static class ScaledFloatingPointDCT // temporal result is saved to +4 shifted indices // because result is saved into the top left 2x2 region of the // input block - block[(ctr * 8) + 0 + 4] = (tmp10 + tmp2) / 2; - block[(ctr * 8) + 3 + 4] = (tmp10 - tmp2) / 2; - block[(ctr * 8) + 1 + 4] = (tmp12 + tmp0) / 2; - block[(ctr * 8) + 2 + 4] = (tmp12 - tmp0) / 2; + block[(ctr * 8) + 0 + 4] = (tmp10 + tmp2) * 0.5F; + block[(ctr * 8) + 3 + 4] = (tmp10 - tmp2) * 0.5F; + block[(ctr * 8) + 1 + 4] = (tmp12 + tmp0) * 0.5F; + block[(ctr * 8) + 2 + 4] = (tmp12 - tmp0) * 0.5F; } for (int ctr = 0; ctr < 4; ctr++) @@ -136,10 +136,10 @@ internal static class ScaledFloatingPointDCT (z4 * FP32_2_562915447); // Save results to the top left 4x4 subregion - block[(ctr * 8) + 0] = MathF.Round(Numerics.Clamp(((tmp10 + tmp2) / 2) + normalizationValue, 0, maxValue)); - block[(ctr * 8) + 3] = MathF.Round(Numerics.Clamp(((tmp10 - tmp2) / 2) + normalizationValue, 0, maxValue)); - block[(ctr * 8) + 1] = MathF.Round(Numerics.Clamp(((tmp12 + tmp0) / 2) + normalizationValue, 0, maxValue)); - block[(ctr * 8) + 2] = MathF.Round(Numerics.Clamp(((tmp12 - tmp0) / 2) + normalizationValue, 0, maxValue)); + block[(ctr * 8) + 0] = MathF.Round(Numerics.Clamp(((tmp10 + tmp2) * 0.5F) + normalizationValue, 0, maxValue)); + block[(ctr * 8) + 3] = MathF.Round(Numerics.Clamp(((tmp10 - tmp2) * 0.5F) + normalizationValue, 0, maxValue)); + block[(ctr * 8) + 1] = MathF.Round(Numerics.Clamp(((tmp12 + tmp0) * 0.5F) + normalizationValue, 0, maxValue)); + block[(ctr * 8) + 2] = MathF.Round(Numerics.Clamp(((tmp12 - tmp0) * 0.5F) + normalizationValue, 0, maxValue)); } } @@ -183,8 +183,8 @@ internal static class ScaledFloatingPointDCT // temporal result is saved to +2 shifted indices // because result is saved into the top left 2x2 region of the // input block - block[(ctr * 8) + 2] = (tmp10 + tmp0) / 4; - block[(ctr * 8) + 3] = (tmp10 - tmp0) / 4; + block[(ctr * 8) + 2] = (tmp10 + tmp0) * 0.25F; + block[(ctr * 8) + 3] = (tmp10 - tmp0) * 0.25F; } for (int ctr = 0; ctr < 2; ctr++) @@ -199,8 +199,8 @@ internal static class ScaledFloatingPointDCT (block[ctr + (8 * 1) + 2] * FP32_3_624509785); // Save results to the top left 2x2 subregion - block[(ctr * 8) + 0] = MathF.Round(Numerics.Clamp(((tmp10 + tmp0) / 4) + normalizationValue, 0, maxValue)); - block[(ctr * 8) + 1] = MathF.Round(Numerics.Clamp(((tmp10 - tmp0) / 4) + normalizationValue, 0, maxValue)); + block[(ctr * 8) + 0] = MathF.Round(Numerics.Clamp(((tmp10 + tmp0) * 0.25F) + normalizationValue, 0, maxValue)); + block[(ctr * 8) + 1] = MathF.Round(Numerics.Clamp(((tmp10 - tmp0) * 0.25F) + normalizationValue, 0, maxValue)); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index ab0521712a..83a828caaf 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -27,21 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg; /// internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals { - /// - /// The only supported precision - /// - private readonly byte[] supportedPrecisions = { 8, 12 }; - - /// - /// The buffer used to temporarily store bytes read from the stream. - /// - private readonly byte[] temp = new byte[2 * 16 * 4]; - - /// - /// The buffer used to read markers from the stream. - /// - private readonly byte[] markerBuffer = new byte[2]; - /// /// Whether the image has an EXIF marker. /// @@ -139,6 +124,12 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals this.skipMetadata = options.GeneralOptions.SkipMetadata; } + /// + /// Gets the only supported precisions + /// + // Refers to assembly's static data segment, no allocation occurs. + private static ReadOnlySpan SupportedPrecisions => new byte[] { 8, 12 }; + /// public DecoderOptions Options { get; } @@ -235,7 +226,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals this.InitDerivedMetadataProperties(); Size pixelSize = this.Frame.PixelSize; - return new ImageInfo(new PixelTypeInfo(this.Frame.BitsPerPixel), pixelSize.Width, pixelSize.Height, this.Metadata); + return new ImageInfo(new PixelTypeInfo(this.Frame.BitsPerPixel), new(pixelSize.Width, pixelSize.Height), this.Metadata); } /// @@ -257,24 +248,26 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals using MemoryStream ms = new(tableBytes); using BufferedReadStream stream = new(this.configuration, ms); + Span markerBuffer = stackalloc byte[2]; + // Check for the Start Of Image marker. - int bytesRead = stream.Read(this.markerBuffer, 0, 2); - JpegFileMarker fileMarker = new(this.markerBuffer[1], 0); + int bytesRead = stream.Read(markerBuffer); + JpegFileMarker fileMarker = new(markerBuffer[1], 0); if (fileMarker.Marker != JpegConstants.Markers.SOI) { JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker."); } // Read next marker. - bytesRead = stream.Read(this.markerBuffer, 0, 2); - fileMarker = new JpegFileMarker(this.markerBuffer[1], (int)stream.Position - 2); + bytesRead = stream.Read(markerBuffer); + fileMarker = new JpegFileMarker(markerBuffer[1], (int)stream.Position - 2); while (fileMarker.Marker != JpegConstants.Markers.EOI || (fileMarker.Marker == JpegConstants.Markers.EOI && fileMarker.Invalid)) { if (!fileMarker.Invalid) { // Get the marker length. - int markerContentByteSize = this.ReadUint16(stream) - 2; + int markerContentByteSize = ReadUint16(stream, markerBuffer) - 2; // Check whether the stream actually has enough bytes to read // markerContentByteSize is always positive so we cast @@ -297,7 +290,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals this.ProcessDefineQuantizationTablesMarker(stream, markerContentByteSize); break; case JpegConstants.Markers.DRI: - this.ProcessDefineRestartIntervalMarker(stream, markerContentByteSize); + this.ProcessDefineRestartIntervalMarker(stream, markerContentByteSize, markerBuffer); break; case JpegConstants.Markers.EOI: return; @@ -305,13 +298,13 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals } // Read next marker. - bytesRead = stream.Read(this.markerBuffer, 0, 2); + bytesRead = stream.Read(markerBuffer); if (bytesRead != 2) { JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read marker"); } - fileMarker = new JpegFileMarker(this.markerBuffer[1], 0); + fileMarker = new JpegFileMarker(markerBuffer[1], 0); } } @@ -329,9 +322,11 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals this.Metadata = new ImageMetadata(); + Span markerBuffer = stackalloc byte[2]; + // Check for the Start Of Image marker. - stream.Read(this.markerBuffer, 0, 2); - JpegFileMarker fileMarker = new(this.markerBuffer[1], 0); + stream.Read(markerBuffer); + JpegFileMarker fileMarker = new(markerBuffer[1], 0); if (fileMarker.Marker != JpegConstants.Markers.SOI) { JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker."); @@ -349,7 +344,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals if (!fileMarker.Invalid) { // Get the marker length. - int markerContentByteSize = this.ReadUint16(stream) - 2; + int markerContentByteSize = ReadUint16(stream, markerBuffer) - 2; // Check whether stream actually has enough bytes to read // markerContentByteSize is always positive so we cast @@ -446,7 +441,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals } else { - this.ProcessDefineRestartIntervalMarker(stream, markerContentByteSize); + this.ProcessDefineRestartIntervalMarker(stream, markerContentByteSize, markerBuffer); } break; @@ -755,8 +750,10 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals return; } - stream.Read(this.temp, 0, JFifMarker.Length); - if (!JFifMarker.TryParse(this.temp, out this.jFif)) + Span temp = stackalloc byte[2 * 16 * 4]; + + stream.Read(temp, 0, JFifMarker.Length); + if (!JFifMarker.TryParse(temp, out this.jFif)) { JpegThrowHelper.ThrowNotSupportedException("Unknown App0 Marker - Expected JFIF."); } @@ -796,11 +793,13 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals JpegThrowHelper.ThrowInvalidImageContentException("Bad App1 Marker length."); } + Span temp = stackalloc byte[2 * 16 * 4]; + // XMP marker is the longer then the EXIF marker, so first try read the EXIF marker bytes. - stream.Read(this.temp, 0, exifMarkerLength); + stream.Read(temp, 0, exifMarkerLength); remaining -= exifMarkerLength; - if (ProfileResolver.IsProfile(this.temp, ProfileResolver.ExifMarker)) + if (ProfileResolver.IsProfile(temp, ProfileResolver.ExifMarker)) { this.hasExif = true; byte[] profile = new byte[remaining]; @@ -819,7 +818,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals remaining = 0; } - if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker[..exifMarkerLength])) + if (ProfileResolver.IsProfile(temp, ProfileResolver.XmpMarker[..exifMarkerLength])) { const int remainingXmpMarkerBytes = xmpMarkerLength - exifMarkerLength; if (remaining < remainingXmpMarkerBytes || this.skipMetadata) @@ -829,9 +828,9 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals return; } - stream.Read(this.temp, exifMarkerLength, remainingXmpMarkerBytes); + stream.Read(temp, exifMarkerLength, remainingXmpMarkerBytes); remaining -= remainingXmpMarkerBytes; - if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker)) + if (ProfileResolver.IsProfile(temp, ProfileResolver.XmpMarker)) { this.hasXmp = true; byte[] profile = new byte[remaining]; @@ -870,8 +869,8 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals return; } - byte[] identifier = new byte[icclength]; - stream.Read(identifier, 0, icclength); + Span identifier = stackalloc byte[icclength]; + stream.Read(identifier); remaining -= icclength; // We have read it by this point if (ProfileResolver.IsProfile(identifier, ProfileResolver.IccMarker)) @@ -911,13 +910,13 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals return; } - stream.Read(this.temp, 0, ProfileResolver.AdobePhotoshopApp13Marker.Length); + Span temp = stackalloc byte[2 * 16 * 4]; + stream.Read(temp, 0, ProfileResolver.AdobePhotoshopApp13Marker.Length); remaining -= ProfileResolver.AdobePhotoshopApp13Marker.Length; - if (ProfileResolver.IsProfile(this.temp, ProfileResolver.AdobePhotoshopApp13Marker)) + if (ProfileResolver.IsProfile(temp, ProfileResolver.AdobePhotoshopApp13Marker)) { - byte[] resourceBlockData = new byte[remaining]; - stream.Read(resourceBlockData, 0, remaining); - Span blockDataSpan = resourceBlockData.AsSpan(); + Span blockDataSpan = remaining <= 128 ? stackalloc byte[remaining] : new byte[remaining]; + stream.Read(blockDataSpan); while (blockDataSpan.Length > 12) { @@ -1047,10 +1046,12 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals return; } - stream.Read(this.temp, 0, markerLength); + Span temp = stackalloc byte[2 * 16 * 4]; + + stream.Read(temp, 0, markerLength); remaining -= markerLength; - if (AdobeMarker.TryParse(this.temp, out this.adobe)) + if (AdobeMarker.TryParse(temp, out this.adobe)) { this.hasAdobeMarker = true; } @@ -1072,6 +1073,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals private void ProcessDefineQuantizationTablesMarker(BufferedReadStream stream, int remaining) { JpegMetadata jpegMetadata = this.Metadata.GetFormatMetadata(JpegFormat.Instance); + Span temp = stackalloc byte[2 * 16 * 4]; while (remaining > 0) { @@ -1102,13 +1104,13 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.DQT), remaining); } - stream.Read(this.temp, 0, 64); + stream.Read(temp, 0, 64); remaining -= 64; // Parsing quantization table & saving it in natural order for (int j = 0; j < 64; j++) { - table[ZigZag.ZigZagOrder[j]] = this.temp[j]; + table[ZigZag.ZigZagOrder[j]] = temp[j]; } break; @@ -1121,13 +1123,13 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.DQT), remaining); } - stream.Read(this.temp, 0, 128); + stream.Read(temp, 0, 128); remaining -= 128; // Parsing quantization table & saving it in natural order for (int j = 0; j < 64; j++) { - table[ZigZag.ZigZagOrder[j]] = (this.temp[2 * j] << 8) | this.temp[(2 * j) + 1]; + table[ZigZag.ZigZagOrder[j]] = (temp[2 * j] << 8) | temp[(2 * j) + 1]; } break; @@ -1174,28 +1176,30 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals JpegThrowHelper.ThrowInvalidImageContentException("Multiple SOF markers. Only single frame jpegs supported."); } + Span temp = stackalloc byte[2 * 16 * 4]; + // Read initial marker definitions. const int length = 6; - int bytesRead = stream.Read(this.temp, 0, length); + int bytesRead = stream.Read(temp, 0, length); if (bytesRead != length) { JpegThrowHelper.ThrowInvalidImageContentException("SOF marker does not contain enough data."); } // 1 byte: Bits/sample precision. - byte precision = this.temp[0]; + byte precision = temp[0]; // Validate: only 8-bit and 12-bit precisions are supported. - if (Array.IndexOf(this.supportedPrecisions, precision) == -1) + if (SupportedPrecisions.IndexOf(precision) < 0) { JpegThrowHelper.ThrowInvalidImageContentException("Only 8-Bit and 12-Bit precision is supported."); } // 2 byte: Height - int frameHeight = (this.temp[1] << 8) | this.temp[2]; + int frameHeight = (temp[1] << 8) | temp[2]; // 2 byte: Width - int frameWidth = (this.temp[3] << 8) | this.temp[4]; + int frameWidth = (temp[3] << 8) | temp[4]; // Validate: width/height > 0 (they are upper-bounded by 2 byte max value so no need to check that). if (frameHeight == 0 || frameWidth == 0) @@ -1204,7 +1208,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals } // 1 byte: Number of components. - byte componentCount = this.temp[5]; + byte componentCount = temp[5]; // Validate: componentCount more than 4 can lead to a buffer overflow during stream // reading so we must limit it to 4. @@ -1227,7 +1231,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals } // components*3 bytes: component data - stream.Read(this.temp, 0, remaining); + stream.Read(temp, 0, remaining); // No need to pool this. They max out at 4 this.Frame.ComponentIds = new byte[componentCount]; @@ -1240,10 +1244,10 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals for (int i = 0; i < this.Frame.Components.Length; i++) { // 1 byte: component identifier - byte componentId = this.temp[index]; + byte componentId = temp[index]; // 1 byte: component sampling factors - byte hv = this.temp[index + 1]; + byte hv = temp[index + 1]; int h = (hv >> 4) & 15; int v = hv & 15; @@ -1270,7 +1274,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals } // 1 byte: quantization table destination selector - byte quantTableIndex = this.temp[index + 2]; + byte quantTableIndex = temp[index + 2]; // Validate: 0-3 range if (quantTableIndex > 3) @@ -1379,7 +1383,8 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals /// /// The input stream. /// The remaining bytes in the segment block. - private void ProcessDefineRestartIntervalMarker(BufferedReadStream stream, int remaining) + /// Scratch buffer. + private void ProcessDefineRestartIntervalMarker(BufferedReadStream stream, int remaining, Span markerBuffer) { if (remaining != 2) { @@ -1388,7 +1393,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals // Save the reset interval, because it can come before or after the SOF marker. // If the reset interval comes after the SOF marker, the scanDecoder has not been created. - this.resetInterval = this.ReadUint16(stream); + this.resetInterval = ReadUint16(stream, markerBuffer); if (this.scanDecoder != null) { @@ -1425,14 +1430,16 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.SOS), remaining); } + Span temp = stackalloc byte[2 * 16 * 4]; + // selectorsCount*2 bytes: component index + huffman tables indices - stream.Read(this.temp, 0, selectorsBytes); + stream.Read(temp, 0, selectorsBytes); this.Frame.Interleaved = this.Frame.ComponentCount == selectorsCount; for (int i = 0; i < selectorsBytes; i += 2) { // 1 byte: Component id - int componentSelectorId = this.temp[i]; + int componentSelectorId = temp[i]; int componentIndex = -1; for (int j = 0; j < this.Frame.ComponentIds.Length; j++) @@ -1459,7 +1466,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals // 1 byte: Huffman table selectors. // 4 bits - dc // 4 bits - ac - int tableSpec = this.temp[i + 1]; + int tableSpec = temp[i + 1]; int dcTableIndex = tableSpec >> 4; int acTableIndex = tableSpec & 15; @@ -1475,17 +1482,17 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals } // 3 bytes: Progressive scan decoding data. - int bytesRead = stream.Read(this.temp, 0, 3); + int bytesRead = stream.Read(temp, 0, 3); if (bytesRead != 3) { JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read progressive scan decoding data"); } - this.scanDecoder.SpectralStart = this.temp[0]; + this.scanDecoder.SpectralStart = temp[0]; - this.scanDecoder.SpectralEnd = this.temp[1]; + this.scanDecoder.SpectralEnd = temp[1]; - int successiveApproximation = this.temp[2]; + int successiveApproximation = temp[2]; this.scanDecoder.SuccessiveHigh = successiveApproximation >> 4; this.scanDecoder.SuccessiveLow = successiveApproximation & 15; @@ -1501,16 +1508,17 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals /// Reads a from the stream advancing it by two bytes. /// /// The input stream. + /// The scratch buffer used for reading from the stream. /// The [MethodImpl(InliningOptions.ShortMethod)] - private ushort ReadUint16(BufferedReadStream stream) + private static ushort ReadUint16(BufferedReadStream stream, Span markerBuffer) { - int bytesRead = stream.Read(this.markerBuffer, 0, 2); + int bytesRead = stream.Read(markerBuffer, 0, 2); if (bytesRead != 2) { JpegThrowHelper.ThrowInvalidImageContentException("jpeg stream does not contain enough data, could not read ushort."); } - return BinaryPrimitives.ReadUInt16BigEndian(this.markerBuffer); + return BinaryPrimitives.ReadUInt16BigEndian(markerBuffer); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 1d06333e30..95f7fde32c 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -25,11 +25,6 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// private static readonly JpegFrameConfig[] FrameConfigs = CreateFrameConfigs(); - /// - /// A scratch buffer to reduce allocations. - /// - private readonly byte[] buffer = new byte[20]; - private readonly JpegEncoder encoder; /// @@ -67,6 +62,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals cancellationToken.ThrowIfCancellationRequested(); this.outputStream = stream; + Span buffer = stackalloc byte[20]; ImageMetadata metadata = image.Metadata; JpegMetadata jpegMetadata = metadata.GetJpegMetadata(); @@ -76,39 +72,39 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals using JpegFrame frame = new(image, frameConfig, interleaved); // Write the Start Of Image marker. - this.WriteStartOfImage(); + this.WriteStartOfImage(buffer); // Write APP0 marker if (frameConfig.AdobeColorTransformMarkerFlag is null) { - this.WriteJfifApplicationHeader(metadata); + this.WriteJfifApplicationHeader(metadata, buffer); } // Write APP14 marker with adobe color extension else { - this.WriteApp14Marker(frameConfig.AdobeColorTransformMarkerFlag.Value); + this.WriteApp14Marker(frameConfig.AdobeColorTransformMarkerFlag.Value, buffer); } // Write Exif, XMP, ICC and IPTC profiles - this.WriteProfiles(metadata); + this.WriteProfiles(metadata, buffer); // Write the image dimensions. - this.WriteStartOfFrame(image.Width, image.Height, frameConfig); + this.WriteStartOfFrame(image.Width, image.Height, frameConfig, buffer); // Write the Huffman tables. HuffmanScanEncoder scanEncoder = new(frame.BlocksPerMcu, stream); - this.WriteDefineHuffmanTables(frameConfig.HuffmanTables, scanEncoder); + this.WriteDefineHuffmanTables(frameConfig.HuffmanTables, scanEncoder, buffer); // Write the quantization tables. - this.WriteDefineQuantizationTables(frameConfig.QuantizationTables, this.encoder.Quality, jpegMetadata); + this.WriteDefineQuantizationTables(frameConfig.QuantizationTables, this.encoder.Quality, jpegMetadata, buffer); // Write scans with actual pixel data using SpectralConverter spectralConverter = new(frame, image, this.QuantizationTables); - this.WriteHuffmanScans(frame, frameConfig, spectralConverter, scanEncoder, cancellationToken); + this.WriteHuffmanScans(frame, frameConfig, spectralConverter, scanEncoder, buffer, cancellationToken); // Write the End Of Image marker. - this.WriteEndOfImageMarker(); + this.WriteEndOfImageMarker(buffer); stream.Flush(); } @@ -116,58 +112,59 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// /// Write the start of image marker. /// - private void WriteStartOfImage() + private void WriteStartOfImage(Span buffer) { // Markers are always prefixed with 0xff. - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.SOI; + buffer[1] = JpegConstants.Markers.SOI; + buffer[0] = JpegConstants.Markers.XFF; - this.outputStream.Write(this.buffer, 0, 2); + this.outputStream.Write(buffer, 0, 2); } /// /// Writes the application header containing the JFIF identifier plus extra data. /// /// The image metadata. - private void WriteJfifApplicationHeader(ImageMetadata meta) + /// Temporary buffer. + private void WriteJfifApplicationHeader(ImageMetadata meta, Span buffer) { - // Write the JFIF headers - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.APP0; // Application Marker - this.buffer[2] = 0x00; - this.buffer[3] = 0x10; - this.buffer[4] = 0x4a; // J - this.buffer[5] = 0x46; // F - this.buffer[6] = 0x49; // I - this.buffer[7] = 0x46; // F - this.buffer[8] = 0x00; // = "JFIF",'\0' - this.buffer[9] = 0x01; // versionhi - this.buffer[10] = 0x01; // versionlo + // Write the JFIF headers (highest index first to avoid additional bound checks) + buffer[10] = 0x01; // versionlo + buffer[0] = JpegConstants.Markers.XFF; + buffer[1] = JpegConstants.Markers.APP0; // Application Marker + buffer[2] = 0x00; + buffer[3] = 0x10; + buffer[4] = 0x4a; // J + buffer[5] = 0x46; // F + buffer[6] = 0x49; // I + buffer[7] = 0x46; // F + buffer[8] = 0x00; // = "JFIF",'\0' + buffer[9] = 0x01; // versionhi // Resolution. Big Endian - Span hResolution = this.buffer.AsSpan(12, 2); - Span vResolution = this.buffer.AsSpan(14, 2); + Span hResolution = buffer.Slice(12, 2); + Span vResolution = buffer.Slice(14, 2); if (meta.ResolutionUnits == PixelResolutionUnit.PixelsPerMeter) { // Scale down to PPI - this.buffer[11] = (byte)PixelResolutionUnit.PixelsPerInch; // xyunits + buffer[11] = (byte)PixelResolutionUnit.PixelsPerInch; // xyunits BinaryPrimitives.WriteInt16BigEndian(hResolution, (short)Math.Round(UnitConverter.MeterToInch(meta.HorizontalResolution))); BinaryPrimitives.WriteInt16BigEndian(vResolution, (short)Math.Round(UnitConverter.MeterToInch(meta.VerticalResolution))); } else { // We can simply pass the value. - this.buffer[11] = (byte)meta.ResolutionUnits; // xyunits + buffer[11] = (byte)meta.ResolutionUnits; // xyunits BinaryPrimitives.WriteInt16BigEndian(hResolution, (short)Math.Round(meta.HorizontalResolution)); BinaryPrimitives.WriteInt16BigEndian(vResolution, (short)Math.Round(meta.VerticalResolution)); } // No thumbnail - this.buffer[16] = 0x00; // Thumbnail width - this.buffer[17] = 0x00; // Thumbnail height + buffer[17] = 0x00; // Thumbnail height + buffer[16] = 0x00; // Thumbnail width - this.outputStream.Write(this.buffer, 0, 18); + this.outputStream.Write(buffer, 0, 18); } /// @@ -175,8 +172,9 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// /// The table configuration. /// The scan encoder. + /// Temporary buffer. /// is . - private void WriteDefineHuffmanTables(JpegHuffmanTableConfig[] tableConfigs, HuffmanScanEncoder scanEncoder) + private void WriteDefineHuffmanTables(JpegHuffmanTableConfig[] tableConfigs, HuffmanScanEncoder scanEncoder, Span buffer) { if (tableConfigs is null) { @@ -190,7 +188,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals markerlen += 1 + 16 + tableConfigs[i].Table.Values.Length; } - this.WriteMarkerHeader(JpegConstants.Markers.DHT, markerlen); + this.WriteMarkerHeader(JpegConstants.Markers.DHT, markerlen, buffer); for (int i = 0; i < tableConfigs.Length; i++) { JpegHuffmanTableConfig tableConfig = tableConfigs[i]; @@ -208,37 +206,39 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// Writes the APP14 marker to indicate the image is in RGB color space. /// /// The color transform byte. - private void WriteApp14Marker(byte colorTransform) + /// Temporary buffer. + private void WriteApp14Marker(byte colorTransform, Span buffer) { - this.WriteMarkerHeader(JpegConstants.Markers.APP14, 2 + Components.Decoder.AdobeMarker.Length); + this.WriteMarkerHeader(JpegConstants.Markers.APP14, 2 + Components.Decoder.AdobeMarker.Length, buffer); - // Identifier: ASCII "Adobe". - this.buffer[0] = 0x41; - this.buffer[1] = 0x64; - this.buffer[2] = 0x6F; - this.buffer[3] = 0x62; - this.buffer[4] = 0x65; + // Identifier: ASCII "Adobe" (highest index first to avoid additional bound checks). + buffer[4] = 0x65; + buffer[0] = 0x41; + buffer[1] = 0x64; + buffer[2] = 0x6F; + buffer[3] = 0x62; // Version, currently 100. - BinaryPrimitives.WriteInt16BigEndian(this.buffer.AsSpan(5, 2), 100); + BinaryPrimitives.WriteInt16BigEndian(buffer.Slice(5, 2), 100); // Flags0 - BinaryPrimitives.WriteInt16BigEndian(this.buffer.AsSpan(7, 2), 0); + BinaryPrimitives.WriteInt16BigEndian(buffer.Slice(7, 2), 0); // Flags1 - BinaryPrimitives.WriteInt16BigEndian(this.buffer.AsSpan(9, 2), 0); + BinaryPrimitives.WriteInt16BigEndian(buffer.Slice(9, 2), 0); // Color transform byte - this.buffer[11] = colorTransform; + buffer[11] = colorTransform; - this.outputStream.Write(this.buffer.AsSpan(0, 12)); + this.outputStream.Write(buffer.Slice(0, 12)); } /// /// Writes the EXIF profile. /// /// The exif profile. - private void WriteExifProfile(ExifProfile exifProfile) + /// Temporary buffer. + private void WriteExifProfile(ExifProfile exifProfile, Span buffer) { if (exifProfile is null || exifProfile.Values.Count == 0) { @@ -262,7 +262,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals int app1Length = bytesToWrite + 2; // Write the app marker, EXIF marker, and data - this.WriteApp1Header(app1Length); + this.WriteApp1Header(app1Length, buffer); this.outputStream.Write(Components.Decoder.ProfileResolver.ExifMarker); this.outputStream.Write(data, 0, bytesToWrite - exifMarkerLength); remaining -= bytesToWrite; @@ -273,7 +273,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals bytesToWrite = remaining > maxBytesWithExifId ? maxBytesWithExifId : remaining; app1Length = bytesToWrite + 2 + exifMarkerLength; - this.WriteApp1Header(app1Length); + this.WriteApp1Header(app1Length, buffer); // Write Exif00 marker this.outputStream.Write(Components.Decoder.ProfileResolver.ExifMarker); @@ -289,10 +289,11 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// Writes the IPTC metadata. /// /// The iptc metadata to write. + /// Temporary buffer. /// /// Thrown if the IPTC profile size exceeds the limit of 65533 bytes. /// - private void WriteIptcProfile(IptcProfile iptcProfile) + private void WriteIptcProfile(IptcProfile iptcProfile, Span buffer) { const int maxBytes = 65533; if (iptcProfile is null || !iptcProfile.Values.Any()) @@ -316,14 +317,14 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals Components.Decoder.ProfileResolver.AdobeImageResourceBlockMarker.Length + Components.Decoder.ProfileResolver.AdobeIptcMarker.Length + 2 + 4 + data.Length; - this.WriteAppHeader(app13Length, JpegConstants.Markers.APP13); + this.WriteAppHeader(app13Length, JpegConstants.Markers.APP13, buffer); this.outputStream.Write(Components.Decoder.ProfileResolver.AdobePhotoshopApp13Marker); this.outputStream.Write(Components.Decoder.ProfileResolver.AdobeImageResourceBlockMarker); this.outputStream.Write(Components.Decoder.ProfileResolver.AdobeIptcMarker); this.outputStream.WriteByte(0); // a empty pascal string (padded to make size even) this.outputStream.WriteByte(0); - BinaryPrimitives.WriteInt32BigEndian(this.buffer, data.Length); - this.outputStream.Write(this.buffer, 0, 4); + BinaryPrimitives.WriteInt32BigEndian(buffer, data.Length); + this.outputStream.Write(buffer, 0, 4); this.outputStream.Write(data, 0, data.Length); } @@ -331,10 +332,11 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// Writes the XMP metadata. /// /// The XMP metadata to write. + /// Temporary buffer. /// /// Thrown if the XMP profile size exceeds the limit of 65533 bytes. /// - private void WriteXmpProfile(XmpProfile xmpProfile) + private void WriteXmpProfile(XmpProfile xmpProfile, Span buffer) { if (xmpProfile is null) { @@ -367,7 +369,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals dataLength -= length; int app1Length = 2 + Components.Decoder.ProfileResolver.XmpMarker.Length + length; - this.WriteApp1Header(app1Length); + this.WriteApp1Header(app1Length, buffer); this.outputStream.Write(Components.Decoder.ProfileResolver.XmpMarker); this.outputStream.Write(data, offset, length); @@ -379,32 +381,35 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// Writes the App1 header. /// /// The length of the data the app1 marker contains. - private void WriteApp1Header(int app1Length) - => this.WriteAppHeader(app1Length, JpegConstants.Markers.APP1); + /// Temporary buffer. + private void WriteApp1Header(int app1Length, Span buffer) + => this.WriteAppHeader(app1Length, JpegConstants.Markers.APP1, buffer); /// /// Writes a AppX header. /// /// The length of the data the app marker contains. /// The app marker to write. - private void WriteAppHeader(int length, byte appMarker) + /// Temporary buffer. + private void WriteAppHeader(int length, byte appMarker, Span buffer) { - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = appMarker; - this.buffer[2] = (byte)((length >> 8) & 0xFF); - this.buffer[3] = (byte)(length & 0xFF); + buffer[0] = JpegConstants.Markers.XFF; + buffer[1] = appMarker; + buffer[2] = (byte)((length >> 8) & 0xFF); + buffer[3] = (byte)(length & 0xFF); - this.outputStream.Write(this.buffer, 0, 4); + this.outputStream.Write(buffer, 0, 4); } /// /// Writes the ICC profile. /// /// The ICC profile to write. + /// Temporary buffer. /// /// Thrown if any of the ICC profiles size exceeds the limit. /// - private void WriteIccProfile(IccProfile iccProfile) + private void WriteIccProfile(IccProfile iccProfile, Span buffer) { if (iccProfile is null) { @@ -446,30 +451,31 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals dataLength -= length; - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.APP2; // Application Marker + buffer[0] = JpegConstants.Markers.XFF; + buffer[1] = JpegConstants.Markers.APP2; // Application Marker int markerLength = length + 16; - this.buffer[2] = (byte)((markerLength >> 8) & 0xFF); - this.buffer[3] = (byte)(markerLength & 0xFF); - - this.outputStream.Write(this.buffer, 0, 4); - - this.buffer[0] = (byte)'I'; - this.buffer[1] = (byte)'C'; - this.buffer[2] = (byte)'C'; - this.buffer[3] = (byte)'_'; - this.buffer[4] = (byte)'P'; - this.buffer[5] = (byte)'R'; - this.buffer[6] = (byte)'O'; - this.buffer[7] = (byte)'F'; - this.buffer[8] = (byte)'I'; - this.buffer[9] = (byte)'L'; - this.buffer[10] = (byte)'E'; - this.buffer[11] = 0x00; - this.buffer[12] = (byte)current; // The position within the collection. - this.buffer[13] = (byte)count; // The total number of profiles. - - this.outputStream.Write(this.buffer, 0, iccOverheadLength); + buffer[2] = (byte)((markerLength >> 8) & 0xFF); + buffer[3] = (byte)(markerLength & 0xFF); + + this.outputStream.Write(buffer, 0, 4); + + // We write the highest index first, to have only one bound check. + buffer[13] = (byte)count; // The total number of profiles. + buffer[12] = (byte)current; // The position within the collection. + buffer[11] = 0x00; + buffer[0] = (byte)'I'; + buffer[1] = (byte)'C'; + buffer[2] = (byte)'C'; + buffer[3] = (byte)'_'; + buffer[4] = (byte)'P'; + buffer[5] = (byte)'R'; + buffer[6] = (byte)'O'; + buffer[7] = (byte)'F'; + buffer[8] = (byte)'I'; + buffer[9] = (byte)'L'; + buffer[10] = (byte)'E'; + + this.outputStream.Write(buffer, 0, iccOverheadLength); this.outputStream.Write(data, offset, length); current++; @@ -481,7 +487,8 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// Writes the metadata profiles to the image. /// /// The image metadata. - private void WriteProfiles(ImageMetadata metadata) + /// Temporary buffer. + private void WriteProfiles(ImageMetadata metadata, Span buffer) { if (metadata is null) { @@ -494,10 +501,10 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals // - APP2 ICC // - APP13 IPTC metadata.SyncProfiles(); - this.WriteExifProfile(metadata.ExifProfile); - this.WriteXmpProfile(metadata.XmpProfile); - this.WriteIccProfile(metadata.IccProfile); - this.WriteIptcProfile(metadata.IptcProfile); + this.WriteExifProfile(metadata.ExifProfile, buffer); + this.WriteXmpProfile(metadata.XmpProfile, buffer); + this.WriteIccProfile(metadata.IccProfile, buffer); + this.WriteIptcProfile(metadata.IptcProfile, buffer); } /// @@ -506,25 +513,26 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// The frame width. /// The frame height. /// The frame configuration. - private void WriteStartOfFrame(int width, int height, JpegFrameConfig frame) + /// Temporary buffer. + private void WriteStartOfFrame(int width, int height, JpegFrameConfig frame, Span buffer) { JpegComponentConfig[] components = frame.Components; // Length (high byte, low byte), 8 + components * 3. int markerlen = 8 + (3 * components.Length); - this.WriteMarkerHeader(JpegConstants.Markers.SOF0, markerlen); - this.buffer[0] = 8; // Data Precision. 8 for now, 12 and 16 bit jpegs not supported - this.buffer[1] = (byte)(height >> 8); - this.buffer[2] = (byte)(height & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported - this.buffer[3] = (byte)(width >> 8); - this.buffer[4] = (byte)(width & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported - this.buffer[5] = (byte)components.Length; + this.WriteMarkerHeader(JpegConstants.Markers.SOF0, markerlen, buffer); + buffer[5] = (byte)components.Length; + buffer[0] = 8; // Data Precision. 8 for now, 12 and 16 bit jpegs not supported + buffer[1] = (byte)(height >> 8); + buffer[2] = (byte)(height & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported + buffer[3] = (byte)(width >> 8); + buffer[4] = (byte)(width & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported // Components data for (int i = 0; i < components.Length; i++) { int i3 = 3 * i; - Span bufferSpan = this.buffer.AsSpan(i3 + 6, 3); + Span bufferSpan = buffer.Slice(i3 + 6, 3); // Quantization table selector bufferSpan[2] = (byte)components[i].QuantizatioTableIndex; @@ -538,14 +546,15 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals bufferSpan[0] = components[i].Id; } - this.outputStream.Write(this.buffer, 0, (3 * (components.Length - 1)) + 9); + this.outputStream.Write(buffer, 0, (3 * (components.Length - 1)) + 9); } /// /// Writes the StartOfScan marker. /// /// The collecction of component configuration items. - private void WriteStartOfScan(Span components) + /// Temporary buffer. + private void WriteStartOfScan(Span components, Span buffer) { // Write the SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes: // - the marker length "\x00\x0c", @@ -556,14 +565,14 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals // - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for // sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) // should be 0x00, 0x3f, 0x00<<4 | 0x00. - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.SOS; + buffer[1] = JpegConstants.Markers.SOS; + buffer[0] = JpegConstants.Markers.XFF; // Length (high byte, low byte), must be 6 + 2 * (number of components in scan) int sosSize = 6 + (2 * components.Length); - this.buffer[2] = 0x00; - this.buffer[3] = (byte)sosSize; - this.buffer[4] = (byte)components.Length; // Number of components in a scan + buffer[4] = (byte)components.Length; // Number of components in a scan + buffer[3] = (byte)sosSize; + buffer[2] = 0x00; // Components data for (int i = 0; i < components.Length; i++) @@ -571,27 +580,28 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals int i2 = 2 * i; // Id - this.buffer[i2 + 5] = components[i].Id; + buffer[i2 + 5] = components[i].Id; // Table selectors int tableSelectors = (components[i].DcTableSelector << 4) | components[i].AcTableSelector; - this.buffer[i2 + 6] = (byte)tableSelectors; + buffer[i2 + 6] = (byte)tableSelectors; } - this.buffer[sosSize - 1] = 0x00; // Ss - Start of spectral selection. - this.buffer[sosSize] = 0x3f; // Se - End of spectral selection. - this.buffer[sosSize + 1] = 0x00; // Ah + Ah (Successive approximation bit position high + low) - this.outputStream.Write(this.buffer, 0, sosSize + 2); + buffer[sosSize - 1] = 0x00; // Ss - Start of spectral selection. + buffer[sosSize] = 0x3f; // Se - End of spectral selection. + buffer[sosSize + 1] = 0x00; // Ah + Ah (Successive approximation bit position high + low) + this.outputStream.Write(buffer, 0, sosSize + 2); } /// /// Writes the EndOfImage marker. /// - private void WriteEndOfImageMarker() + /// Temporary buffer. + private void WriteEndOfImageMarker(Span buffer) { - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.EOI; - this.outputStream.Write(this.buffer, 0, 2); + buffer[1] = JpegConstants.Markers.EOI; + buffer[0] = JpegConstants.Markers.XFF; + this.outputStream.Write(buffer, 0, 2); } /// @@ -602,12 +612,14 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// The frame configuration. /// The spectral converter. /// The scan encoder. + /// Temporary buffer. /// The cancellation token. private void WriteHuffmanScans( JpegFrame frame, JpegFrameConfig frameConfig, SpectralConverter spectralConverter, HuffmanScanEncoder encoder, + Span buffer, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { @@ -615,14 +627,14 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals { frame.AllocateComponents(fullScan: false); - this.WriteStartOfScan(frameConfig.Components); + this.WriteStartOfScan(frameConfig.Components, buffer); encoder.EncodeScanBaselineSingleComponent(frame.Components[0], spectralConverter, cancellationToken); } else if (frame.Interleaved) { frame.AllocateComponents(fullScan: false); - this.WriteStartOfScan(frameConfig.Components); + this.WriteStartOfScan(frameConfig.Components, buffer); encoder.EncodeScanBaselineInterleaved(frameConfig.EncodingColor, frame, spectralConverter, cancellationToken); } else @@ -633,7 +645,7 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals Span components = frameConfig.Components; for (int i = 0; i < frame.Components.Length; i++) { - this.WriteStartOfScan(components.Slice(i, 1)); + this.WriteStartOfScan(components.Slice(i, 1), buffer); encoder.EncodeScanBaseline(frame.Components[i], cancellationToken); } } @@ -644,14 +656,16 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// /// The marker to write. /// The marker length. - private void WriteMarkerHeader(byte marker, int length) + /// Temporary buffer. + private void WriteMarkerHeader(byte marker, int length, Span buffer) { // Markers are always prefixed with 0xff. - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = marker; - this.buffer[2] = (byte)(length >> 8); - this.buffer[3] = (byte)(length & 0xff); - this.outputStream.Write(this.buffer, 0, 4); + buffer[3] = (byte)(length & 0xff); + buffer[2] = (byte)(length >> 8); + buffer[1] = marker; + buffer[0] = JpegConstants.Markers.XFF; + + this.outputStream.Write(buffer, 0, 4); } /// @@ -668,15 +682,16 @@ internal sealed unsafe partial class JpegEncoderCore : IImageEncoderInternals /// Quantization tables configs. /// Optional quality value from the options. /// Jpeg metadata instance. - private void WriteDefineQuantizationTables(JpegQuantizationTableConfig[] configs, int? optionsQuality, JpegMetadata metadata) + /// Temporary buffer. + private void WriteDefineQuantizationTables(JpegQuantizationTableConfig[] configs, int? optionsQuality, JpegMetadata metadata, Span tmpBuffer) { int dataLen = configs.Length * (1 + Block8x8.Size); // Marker + quantization table lengths. int markerlen = 2 + dataLen; - this.WriteMarkerHeader(JpegConstants.Markers.DQT, markerlen); + this.WriteMarkerHeader(JpegConstants.Markers.DQT, markerlen, tmpBuffer); - byte[] buffer = new byte[dataLen]; + Span buffer = dataLen <= 256 ? stackalloc byte[dataLen] : new byte[dataLen]; int offset = 0; Block8x8F workspaceBlock = default; diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs index 80db009a44..e1bc5be6e8 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs @@ -88,7 +88,7 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals // BlackAndWhite pixels are encoded into a byte. int bitsPerPixel = this.componentType == PbmComponentType.Short ? 16 : 8; - return new ImageInfo(new PixelTypeInfo(bitsPerPixel), this.pixelSize.Width, this.pixelSize.Height, this.metadata); + return new ImageInfo(new PixelTypeInfo(bitsPerPixel), new(this.pixelSize.Width, this.pixelSize.Height), this.metadata); } /// diff --git a/src/ImageSharp/Formats/Png/Adam7.cs b/src/ImageSharp/Formats/Png/Adam7.cs index f52a66c265..8310ca64c8 100644 --- a/src/ImageSharp/Formats/Png/Adam7.cs +++ b/src/ImageSharp/Formats/Png/Adam7.cs @@ -67,16 +67,22 @@ internal static class Adam7 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ComputeColumns(int width, int passIndex) { - switch (passIndex) + uint w = (uint)width; + + uint result = passIndex switch { - case 0: return (width + 7) / 8; - case 1: return (width + 3) / 8; - case 2: return (width + 3) / 4; - case 3: return (width + 1) / 4; - case 4: return (width + 1) / 2; - case 5: return width / 2; - case 6: return width; - default: throw new ArgumentException($"Not a valid pass index: {passIndex}"); - } + 0 => (w + 7) / 8, + 1 => (w + 3) / 8, + 2 => (w + 3) / 4, + 3 => (w + 1) / 4, + 4 => (w + 1) / 2, + 5 => w / 2, + 6 => w, + _ => Throw(passIndex) + }; + + return (int)result; + + static uint Throw(int passIndex) => throw new ArgumentException($"Not a valid pass index: {passIndex}"); } } diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index 34e05d7796..2750aa6808 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -42,7 +42,7 @@ internal static class AverageFilter } else { - DecodeScalar(scanline, previousScanline, bytesPerPixel); + DecodeScalar(scanline, previousScanline, (uint)bytesPerPixel); } } @@ -56,7 +56,7 @@ internal static class AverageFilter Vector128 ones = Vector128.Create((byte)1); int rb = scanline.Length; - nint offset = 1; + nuint offset = 1; while (rb >= 4) { ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); @@ -88,7 +88,7 @@ internal static class AverageFilter Vector64 d = Vector64.Zero; int rb = scanline.Length; - int offset = 1; + nuint offset = 1; const int bytesPerBatch = 4; while (rb >= bytesPerBatch) { @@ -108,12 +108,12 @@ internal static class AverageFilter } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void DecodeScalar(Span scanline, Span previousScanline, int bytesPerPixel) + private static void DecodeScalar(Span scanline, Span previousScanline, uint bytesPerPixel) { ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); - nint x = 1; + nuint x = 1; for (; x <= bytesPerPixel /* Note the <= because x starts at 1 */; ++x) { ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); @@ -121,7 +121,7 @@ internal static class AverageFilter scan = (byte)(scan + (above >> 1)); } - for (; x < scanline.Length; ++x) + for (; x < (uint)scanline.Length; ++x) { ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); @@ -139,7 +139,7 @@ internal static class AverageFilter /// The bytes per pixel. /// The sum of the total variance of the filtered row. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(ReadOnlySpan scanline, ReadOnlySpan previousScanline, Span result, int bytesPerPixel, out int sum) + public static void Encode(ReadOnlySpan scanline, ReadOnlySpan previousScanline, Span result, uint bytesPerPixel, out int sum) { DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); @@ -152,7 +152,7 @@ internal static class AverageFilter // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) resultBaseRef = (byte)FilterType.Average; - nint x = 0; + nuint x = 0; for (; x < bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) { byte scan = Unsafe.Add(ref scanBaseRef, x); @@ -169,7 +169,7 @@ internal static class AverageFilter Vector256 sumAccumulator = Vector256.Zero; Vector256 allBitsSet = Avx2.CompareEqual(sumAccumulator, sumAccumulator).AsByte(); - for (nint xLeft = x - bytesPerPixel; x <= scanline.Length - Vector256.Count; xLeft += Vector256.Count) + for (nuint xLeft = x - bytesPerPixel; x <= (uint)(scanline.Length - Vector256.Count); xLeft += (uint)Vector256.Count) { Vector256 scan = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, x)); Vector256 left = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, xLeft)); @@ -179,7 +179,7 @@ internal static class AverageFilter Vector256 res = Avx2.Subtract(scan, avg); Unsafe.As>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = res; // +1 to skip filter type - x += Vector256.Count; + x += (uint)Vector256.Count; sumAccumulator = Avx2.Add(sumAccumulator, Avx2.SumAbsoluteDifferences(Avx2.Abs(res.AsSByte()), zero).AsInt32()); } @@ -192,7 +192,7 @@ internal static class AverageFilter Vector128 sumAccumulator = Vector128.Zero; Vector128 allBitsSet = Sse2.CompareEqual(sumAccumulator, sumAccumulator).AsByte(); - for (nint xLeft = x - bytesPerPixel; x <= scanline.Length - Vector128.Count; xLeft += Vector128.Count) + for (nuint xLeft = x - bytesPerPixel; x <= (uint)(scanline.Length - Vector128.Count); xLeft += (uint)Vector128.Count) { Vector128 scan = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, x)); Vector128 left = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, xLeft)); @@ -202,7 +202,7 @@ internal static class AverageFilter Vector128 res = Sse2.Subtract(scan, avg); Unsafe.As>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = res; // +1 to skip filter type - x += Vector128.Count; + x += (uint)Vector128.Count; Vector128 absRes; if (Ssse3.IsSupported) @@ -221,7 +221,7 @@ internal static class AverageFilter sum += Numerics.EvenReduceSum(sumAccumulator); } - for (nint xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) + for (nuint xLeft = x - bytesPerPixel; x < (uint)scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) { byte scan = Unsafe.Add(ref scanBaseRef, x); byte left = Unsafe.Add(ref scanBaseRef, xLeft); diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index 8998d6bc0f..f2226974c9 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -45,7 +45,7 @@ internal static class PaethFilter } else { - DecodeScalar(scanline, previousScanline, bytesPerPixel); + DecodeScalar(scanline, previousScanline, (uint)bytesPerPixel); } } @@ -59,7 +59,7 @@ internal static class PaethFilter Vector128 d = Vector128.Zero; int rb = scanline.Length; - nint offset = 1; + nuint offset = 1; while (rb >= 4) { ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); @@ -113,7 +113,7 @@ internal static class PaethFilter Vector128 d = Vector128.Zero; int rb = scanline.Length; - nint offset = 1; + nuint offset = 1; const int bytesPerBatch = 4; while (rb >= bytesPerBatch) { @@ -179,14 +179,14 @@ internal static class PaethFilter } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void DecodeScalar(Span scanline, Span previousScanline, int bytesPerPixel) + private static void DecodeScalar(Span scanline, Span previousScanline, uint bytesPerPixel) { ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) - int offset = bytesPerPixel + 1; // Add one because x starts at one. - nint x = 1; + nuint offset = bytesPerPixel + 1; // Add one because x starts at one. + nuint x = 1; for (; x < offset; x++) { ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); @@ -194,7 +194,7 @@ internal static class PaethFilter scan = (byte)(scan + above); } - for (; x < scanline.Length; x++) + for (; x < (uint)scanline.Length; x++) { ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); @@ -226,8 +226,8 @@ internal static class PaethFilter // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp)) resultBaseRef = (byte)FilterType.Paeth; - nint x = 0; - for (; x < bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) + nuint x = 0; + for (; x < (uint)bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) { byte scan = Unsafe.Add(ref scanBaseRef, x); byte above = Unsafe.Add(ref prevBaseRef, x); @@ -242,7 +242,7 @@ internal static class PaethFilter Vector256 zero = Vector256.Zero; Vector256 sumAccumulator = Vector256.Zero; - for (nint xLeft = x - bytesPerPixel; x <= scanline.Length - Vector256.Count; xLeft += Vector256.Count) + for (nuint xLeft = x - (uint)bytesPerPixel; (int)x <= scanline.Length - Vector256.Count; xLeft += (uint)Vector256.Count) { Vector256 scan = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, x)); Vector256 left = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, xLeft)); @@ -251,7 +251,7 @@ internal static class PaethFilter Vector256 res = Avx2.Subtract(scan, PaethPredictor(left, above, upperLeft)); Unsafe.As>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = res; // +1 to skip filter type - x += Vector256.Count; + x += (uint)Vector256.Count; sumAccumulator = Avx2.Add(sumAccumulator, Avx2.SumAbsoluteDifferences(Avx2.Abs(res.AsSByte()), zero).AsInt32()); } @@ -262,7 +262,7 @@ internal static class PaethFilter { Vector sumAccumulator = Vector.Zero; - for (nint xLeft = x - bytesPerPixel; x <= scanline.Length - Vector.Count; xLeft += Vector.Count) + for (nuint xLeft = x - (uint)bytesPerPixel; (int)x <= scanline.Length - Vector.Count; xLeft += (uint)Vector.Count) { Vector scan = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, x)); Vector left = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, xLeft)); @@ -271,7 +271,7 @@ internal static class PaethFilter Vector res = scan - PaethPredictor(left, above, upperLeft); Unsafe.As>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = res; // +1 to skip filter type - x += Vector.Count; + x += (uint)Vector.Count; Numerics.Accumulate(ref sumAccumulator, Vector.AsVectorByte(Vector.Abs(Vector.AsVectorSByte(res)))); } @@ -282,7 +282,7 @@ internal static class PaethFilter } } - for (nint xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) + for (nuint xLeft = x - (uint)bytesPerPixel; (int)x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) { byte scan = Unsafe.Add(ref scanBaseRef, x); byte left = Unsafe.Add(ref scanBaseRef, xLeft); diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index 0f4aa3fcff..d58ac6fb7b 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -36,7 +36,7 @@ internal static class SubFilter } else { - DecodeScalar(scanline, bytesPerPixel); + DecodeScalar(scanline, (uint)bytesPerPixel); } } @@ -47,7 +47,7 @@ internal static class SubFilter Vector128 d = Vector128.Zero; int rb = scanline.Length; - nint offset = 1; + nuint offset = 1; while (rb >= 4) { ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); @@ -70,7 +70,7 @@ internal static class SubFilter Vector64 d = Vector64.Zero; int rb = scanline.Length; - int offset = 1; + nuint offset = 1; const int bytesPerBatch = 4; while (rb >= bytesPerBatch) { @@ -87,14 +87,14 @@ internal static class SubFilter } } - private static void DecodeScalar(Span scanline, int bytesPerPixel) + private static void DecodeScalar(Span scanline, nuint bytesPerPixel) { ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); // Sub(x) + Raw(x-bpp) - nint x = bytesPerPixel + 1; + nuint x = bytesPerPixel + 1; Unsafe.Add(ref scanBaseRef, x); - for (; x < scanline.Length; ++x) + for (; x < (uint)scanline.Length; ++x) { ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); byte prev = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); @@ -121,8 +121,8 @@ internal static class SubFilter // Sub(x) = Raw(x) - Raw(x-bpp) resultBaseRef = (byte)FilterType.Sub; - nint x = 0; - for (; x < bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) + nuint x = 0; + for (; x < (uint)bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) { byte scan = Unsafe.Add(ref scanBaseRef, x); ++x; @@ -136,14 +136,14 @@ internal static class SubFilter Vector256 zero = Vector256.Zero; Vector256 sumAccumulator = Vector256.Zero; - for (nint xLeft = x - bytesPerPixel; x <= scanline.Length - Vector256.Count; xLeft += Vector256.Count) + for (nuint xLeft = x - (uint)bytesPerPixel; x <= (uint)(scanline.Length - Vector256.Count); xLeft += (uint)Vector256.Count) { Vector256 scan = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, x)); Vector256 prev = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, xLeft)); Vector256 res = Avx2.Subtract(scan, prev); Unsafe.As>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = res; // +1 to skip filter type - x += Vector256.Count; + x += (uint)Vector256.Count; sumAccumulator = Avx2.Add(sumAccumulator, Avx2.SumAbsoluteDifferences(Avx2.Abs(res.AsSByte()), zero).AsInt32()); } @@ -154,14 +154,14 @@ internal static class SubFilter { Vector sumAccumulator = Vector.Zero; - for (nint xLeft = x - bytesPerPixel; x <= scanline.Length - Vector.Count; xLeft += Vector.Count) + for (nuint xLeft = x - (uint)bytesPerPixel; x <= (uint)(scanline.Length - Vector.Count); xLeft += (uint)Vector.Count) { Vector scan = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, x)); Vector prev = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, xLeft)); Vector res = scan - prev; Unsafe.As>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = res; // +1 to skip filter type - x += Vector.Count; + x += (uint)Vector.Count; Numerics.Accumulate(ref sumAccumulator, Vector.AsVectorByte(Vector.Abs(Vector.AsVectorSByte(res)))); } @@ -172,7 +172,7 @@ internal static class SubFilter } } - for (nint xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) + for (nuint xLeft = x - (uint)bytesPerPixel; x < (uint)scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) { byte scan = Unsafe.Add(ref scanBaseRef, x); byte prev = Unsafe.Add(ref scanBaseRef, xLeft); diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index f90b129b62..dd3c2d8612 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -52,7 +52,7 @@ internal static class UpFilter // Up(x) + Prior(x) int rb = scanline.Length; - nint offset = 1; + nuint offset = 1; while (rb >= Vector256.Count) { ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); @@ -61,12 +61,12 @@ internal static class UpFilter Unsafe.As>(ref scanRef) = Avx2.Add(up, prior); - offset += Vector256.Count; + offset += (uint)Vector256.Count; rb -= Vector256.Count; } // Handle left over. - for (nint i = offset; i < scanline.Length; i++) + for (nuint i = offset; i < (uint)scanline.Length; i++) { ref byte scan = ref Unsafe.Add(ref scanBaseRef, offset); byte above = Unsafe.Add(ref prevBaseRef, offset); @@ -82,7 +82,7 @@ internal static class UpFilter // Up(x) + Prior(x) int rb = scanline.Length; - nint offset = 1; + nuint offset = 1; while (rb >= Vector128.Count) { ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); @@ -91,12 +91,12 @@ internal static class UpFilter Unsafe.As>(ref scanRef) = Sse2.Add(up, prior); - offset += Vector128.Count; + offset += (uint)Vector128.Count; rb -= Vector128.Count; } // Handle left over. - for (nint i = offset; i < scanline.Length; i++) + for (nuint i = offset; i < (uint)scanline.Length; i++) { ref byte scan = ref Unsafe.Add(ref scanBaseRef, offset); byte above = Unsafe.Add(ref prevBaseRef, offset); @@ -112,7 +112,7 @@ internal static class UpFilter // Up(x) + Prior(x) int rb = scanline.Length; - nint offset = 1; + nuint offset = 1; const int bytesPerBatch = 16; while (rb >= bytesPerBatch) { @@ -127,7 +127,7 @@ internal static class UpFilter } // Handle left over. - for (nint i = offset; i < scanline.Length; i++) + for (nuint i = offset; i < (uint)scanline.Length; i++) { ref byte scan = ref Unsafe.Add(ref scanBaseRef, offset); byte above = Unsafe.Add(ref prevBaseRef, offset); @@ -143,7 +143,7 @@ internal static class UpFilter ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); // Up(x) + Prior(x) - for (nint x = 1; x < scanline.Length; x++) + for (nuint x = 1; x < (uint)scanline.Length; x++) { ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); byte above = Unsafe.Add(ref prevBaseRef, x); @@ -172,21 +172,21 @@ internal static class UpFilter // Up(x) = Raw(x) - Prior(x) resultBaseRef = (byte)FilterType.Up; - nint x = 0; + nuint x = 0; if (Avx2.IsSupported) { Vector256 zero = Vector256.Zero; Vector256 sumAccumulator = Vector256.Zero; - for (; x <= scanline.Length - Vector256.Count;) + for (; x <= (uint)(scanline.Length - Vector256.Count);) { Vector256 scan = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, x)); Vector256 above = Unsafe.As>(ref Unsafe.Add(ref prevBaseRef, x)); Vector256 res = Avx2.Subtract(scan, above); Unsafe.As>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = res; // +1 to skip filter type - x += Vector256.Count; + x += (uint)Vector256.Count; sumAccumulator = Avx2.Add(sumAccumulator, Avx2.SumAbsoluteDifferences(Avx2.Abs(res.AsSByte()), zero).AsInt32()); } @@ -197,14 +197,14 @@ internal static class UpFilter { Vector sumAccumulator = Vector.Zero; - for (; x <= scanline.Length - Vector.Count;) + for (; x <= (uint)(scanline.Length - Vector.Count);) { Vector scan = Unsafe.As>(ref Unsafe.Add(ref scanBaseRef, x)); Vector above = Unsafe.As>(ref Unsafe.Add(ref prevBaseRef, x)); Vector res = scan - above; Unsafe.As>(ref Unsafe.Add(ref resultBaseRef, x + 1)) = res; // +1 to skip filter type - x += Vector.Count; + x += (uint)Vector.Count; Numerics.Accumulate(ref sumAccumulator, Vector.AsVectorByte(Vector.Abs(Vector.AsVectorSByte(res)))); } @@ -215,7 +215,7 @@ internal static class UpFilter } } - for (; x < scanline.Length; /* Note: ++x happens in the body to avoid one add operation */) + for (; x < (uint)scanline.Length; /* Note: ++x happens in the body to avoid one add operation */) { byte scan = Unsafe.Add(ref scanBaseRef, x); byte above = Unsafe.Add(ref prevBaseRef, x); diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 3b2f100e0a..d1d29dca6b 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -28,11 +28,6 @@ namespace SixLabors.ImageSharp.Formats.Png; /// internal sealed class PngDecoderCore : IImageDecoderInternals { - /// - /// Reusable buffer. - /// - private readonly byte[] buffer = new byte[4]; - /// /// The general decoder options. /// @@ -154,9 +149,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.currentStream = stream; this.currentStream.Skip(8); Image image = null; + Span buffer = stackalloc byte[20]; + try { - while (this.TryReadChunk(out PngChunk chunk)) + while (this.TryReadChunk(buffer, out PngChunk chunk)) { try { @@ -252,10 +249,13 @@ internal sealed class PngDecoderCore : IImageDecoderInternals ImageMetadata metadata = new(); PngMetadata pngMetadata = metadata.GetPngMetadata(); this.currentStream = stream; + Span buffer = stackalloc byte[20]; + this.currentStream.Skip(8); + try { - while (this.TryReadChunk(out PngChunk chunk)) + while (this.TryReadChunk(buffer, out PngChunk chunk)) { try { @@ -370,7 +370,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals PngThrowHelper.ThrowNoHeader(); } - return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata); + return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), new(this.header.Width, this.header.Height), metadata); } finally { @@ -414,11 +414,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals for (int i = 0; i < bytesPerScanline; i++) { - byte b = Unsafe.Add(ref sourceRef, i); + byte b = Unsafe.Add(ref sourceRef, (uint)i); for (int shift = 0; shift < 8; shift += bits) { int colorIndex = (b >> (8 - bits - shift)) & mask; - Unsafe.Add(ref resultRef, resultOffset) = (byte)colorIndex; + Unsafe.Add(ref resultRef, (uint)resultOffset) = (byte)colorIndex; resultOffset++; } } @@ -777,8 +777,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.header, scanlineSpan, rowSpan, - this.bytesPerPixel, - this.bytesPerSample); + (uint)this.bytesPerPixel, + (uint)this.bytesPerSample); break; @@ -858,8 +858,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.header, scanlineSpan, rowSpan, - pixelOffset, - increment, + (uint)pixelOffset, + (uint)increment, pngMetadata.HasTransparency, pngMetadata.TransparentL16.GetValueOrDefault(), pngMetadata.TransparentL8.GetValueOrDefault()); @@ -871,10 +871,10 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.header, scanlineSpan, rowSpan, - pixelOffset, - increment, - this.bytesPerPixel, - this.bytesPerSample); + (uint)pixelOffset, + (uint)increment, + (uint)this.bytesPerPixel, + (uint)this.bytesPerSample); break; @@ -883,8 +883,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.header, scanlineSpan, rowSpan, - pixelOffset, - increment, + (uint)pixelOffset, + (uint)increment, this.palette, this.paletteAlpha); @@ -895,8 +895,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.header, scanlineSpan, rowSpan, - pixelOffset, - increment, + (uint)pixelOffset, + (uint)increment, this.bytesPerPixel, this.bytesPerSample, pngMetadata.HasTransparency, @@ -910,8 +910,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.header, scanlineSpan, rowSpan, - pixelOffset, - increment, + (uint)pixelOffset, + (uint)increment, this.bytesPerPixel, this.bytesPerSample); @@ -1401,9 +1401,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals return 0; } - this.currentStream.Read(this.buffer, 0, 4); + Span buffer = stackalloc byte[20]; + + this.currentStream.Read(buffer, 0, 4); - if (this.TryReadChunk(out PngChunk chunk)) + if (this.TryReadChunk(buffer, out PngChunk chunk)) { if (chunk.Type == PngChunkType.Data) { @@ -1420,11 +1422,12 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// /// Reads a chunk from the stream. /// + /// Temporary buffer. /// The image format chunk. /// /// The . /// - private bool TryReadChunk(out PngChunk chunk) + private bool TryReadChunk(Span buffer, out PngChunk chunk) { if (this.nextChunk != null) { @@ -1435,7 +1438,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals return true; } - if (!this.TryReadChunkLength(out int length)) + if (!this.TryReadChunkLength(buffer, out int length)) { chunk = default; @@ -1446,7 +1449,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals while (length < 0 || length > (this.currentStream.Length - this.currentStream.Position)) { // Not a valid chunk so try again until we reach a known chunk. - if (!this.TryReadChunkLength(out length)) + if (!this.TryReadChunkLength(buffer, out length)) { chunk = default; @@ -1454,7 +1457,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals } } - PngChunkType type = this.ReadChunkType(); + PngChunkType type = this.ReadChunkType(buffer); // If we're reading color metadata only we're only interested in the IHDR and tRNS chunks. // We can skip all other chunk data in the stream for better performance. @@ -1471,7 +1474,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals type: type, data: this.ReadChunkData(length)); - this.ValidateChunk(chunk); + this.ValidateChunk(chunk, buffer); // Restore the stream position for IDAT chunks, because it will be decoded later and // was only read to verifying the CRC is correct. @@ -1487,9 +1490,10 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// Validates the png chunk. /// /// The . - private void ValidateChunk(in PngChunk chunk) + /// Temporary buffer. + private void ValidateChunk(in PngChunk chunk, Span buffer) { - uint inputCrc = this.ReadChunkCrc(); + uint inputCrc = this.ReadChunkCrc(buffer); if (chunk.IsCritical) { @@ -1513,13 +1517,14 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// /// Reads the cycle redundancy chunk from the data. /// + /// Temporary buffer. [MethodImpl(InliningOptions.ShortMethod)] - private uint ReadChunkCrc() + private uint ReadChunkCrc(Span buffer) { uint crc = 0; - if (this.currentStream.Read(this.buffer, 0, 4) == 4) + if (this.currentStream.Read(buffer, 0, 4) == 4) { - crc = BinaryPrimitives.ReadUInt32BigEndian(this.buffer); + crc = BinaryPrimitives.ReadUInt32BigEndian(buffer); } return crc; @@ -1554,15 +1559,16 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// /// Identifies the chunk type from the chunk. /// + /// Temporary buffer. /// /// Thrown if the input stream is not valid. /// [MethodImpl(InliningOptions.ShortMethod)] - private PngChunkType ReadChunkType() + private PngChunkType ReadChunkType(Span buffer) { - if (this.currentStream.Read(this.buffer, 0, 4) == 4) + if (this.currentStream.Read(buffer, 0, 4) == 4) { - return (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer); + return (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer); } PngThrowHelper.ThrowInvalidChunkType(); @@ -1574,16 +1580,17 @@ internal sealed class PngDecoderCore : IImageDecoderInternals /// /// Attempts to read the length of the next chunk. /// + /// Temporary buffer. /// The result length. If the return type is this parameter is passed uninitialized. /// /// Whether the length was read. /// [MethodImpl(InliningOptions.ShortMethod)] - private bool TryReadChunkLength(out int result) + private bool TryReadChunkLength(Span buffer, out int result) { - if (this.currentStream.Read(this.buffer, 0, 4) == 4) + if (this.currentStream.Read(buffer, 0, 4) == 4) { - result = BinaryPrimitives.ReadInt32BigEndian(this.buffer); + result = BinaryPrimitives.ReadInt32BigEndian(buffer); return true; } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 5a66fc7d4b..fb1d33277a 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -38,15 +38,10 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable /// private readonly Configuration configuration; - /// - /// Reusable buffer for writing general data. - /// - private readonly byte[] buffer = new byte[8]; - /// /// Reusable buffer for writing chunk data. /// - private readonly byte[] chunkDataBuffer = new byte[16]; + private ScratchBuffer chunkDataBuffer; // mutable struct, don't make readonly /// /// The encoder with options @@ -266,7 +261,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable // Can't map directly to byte array as it's big-endian. for (int x = 0, o = 0; x < luminanceSpan.Length; x++, o += 2) { - L16 luminance = Unsafe.Add(ref luminanceRef, x); + L16 luminance = Unsafe.Add(ref luminanceRef, (uint)x); BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance.PackedValue); } } @@ -306,7 +301,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable // Can't map directly to byte array as it's big endian. for (int x = 0, o = 0; x < laSpan.Length; x++, o += 4) { - La32 la = Unsafe.Add(ref laRef, x); + La32 la = Unsafe.Add(ref laRef, (uint)x); BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), la.L); BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), la.A); } @@ -366,7 +361,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable // Can't map directly to byte array as it's big endian. for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 8) { - Rgba64 rgba = Unsafe.Add(ref rgbaRef, x); + Rgba64 rgba = Unsafe.Add(ref rgbaRef, (uint)x); BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgba.R); BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.G); BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgba.B); @@ -388,7 +383,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable // Can't map directly to byte array as it's big endian. for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 6) { - Rgb48 rgb = Unsafe.Add(ref rgbRef, x); + Rgb48 rgb = Unsafe.Add(ref rgbRef, (uint)x); BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgb.R); BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgb.G); BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgb.B); @@ -455,7 +450,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable break; case PngFilterMethod.Average: - AverageFilter.Encode(this.currentScanline.GetSpan(), this.previousScanline.GetSpan(), filter, this.bytesPerPixel, out int _); + AverageFilter.Encode(this.currentScanline.GetSpan(), this.previousScanline.GetSpan(), filter, (uint)this.bytesPerPixel, out int _); break; case PngFilterMethod.Paeth: @@ -547,7 +542,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable RuntimeUtility.Swap(ref filter, ref attempt); } - AverageFilter.Encode(current, previous, attempt, this.bytesPerPixel, out sum); + AverageFilter.Encode(current, previous, attempt, (uint)this.bytesPerPixel, out sum); if (sum < min) { min = sum; @@ -576,9 +571,9 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable filterMethod: 0, interlaceMethod: this.interlaceMode); - header.WriteTo(this.chunkDataBuffer); + header.WriteTo(this.chunkDataBuffer.Span); - this.WriteChunk(stream, PngChunkType.Header, this.chunkDataBuffer, 0, PngHeader.Size); + this.WriteChunk(stream, PngChunkType.Header, this.chunkDataBuffer.Span, 0, PngHeader.Size); } /// @@ -617,17 +612,17 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable // Loop, assign, and extract alpha values from the palette. for (int i = 0; i < paletteLength; i++) { - Rgba32 rgba = Unsafe.Add(ref rgbaPaletteRef, i); + Rgba32 rgba = Unsafe.Add(ref rgbaPaletteRef, (uint)i); byte alpha = rgba.A; - Unsafe.Add(ref colorTableRef, i) = rgba.Rgb; + Unsafe.Add(ref colorTableRef, (uint)i) = rgba.Rgb; if (alpha > this.encoder.Threshold) { alpha = byte.MaxValue; } hasAlpha = hasAlpha || alpha < byte.MaxValue; - Unsafe.Add(ref alphaTableRef, i) = alpha; + Unsafe.Add(ref alphaTableRef, (uint)i) = alpha; } this.WriteChunk(stream, PngChunkType.Palette, colorTable.GetSpan(), 0, colorTableLength); @@ -652,9 +647,9 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable return; } - PhysicalChunkData.FromMetadata(meta).WriteTo(this.chunkDataBuffer); + PhysicalChunkData.FromMetadata(meta).WriteTo(this.chunkDataBuffer.Span); - this.WriteChunk(stream, PngChunkType.Physical, this.chunkDataBuffer, 0, PhysicalChunkData.Size); + this.WriteChunk(stream, PngChunkType.Physical, this.chunkDataBuffer.Span, 0, PhysicalChunkData.Size); } /// @@ -880,9 +875,9 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable // 4-byte unsigned integer of gamma * 100,000. uint gammaValue = (uint)(this.gamma * 100_000F); - BinaryPrimitives.WriteUInt32BigEndian(this.chunkDataBuffer.AsSpan(0, 4), gammaValue); + BinaryPrimitives.WriteUInt32BigEndian(this.chunkDataBuffer.Span.Slice(0, 4), gammaValue); - this.WriteChunk(stream, PngChunkType.Gamma, this.chunkDataBuffer, 0, 4); + this.WriteChunk(stream, PngChunkType.Gamma, this.chunkDataBuffer.Span, 0, 4); } } @@ -899,7 +894,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable return; } - Span alpha = this.chunkDataBuffer.AsSpan(); + Span alpha = this.chunkDataBuffer.Span; if (pngMetadata.ColorType == PngColorType.Rgb) { if (pngMetadata.TransparentRgb48.HasValue && this.use16Bit) @@ -909,7 +904,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(2, 2), rgb.G); BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(4, 2), rgb.B); - this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6); + this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer.Span, 0, 6); } else if (pngMetadata.TransparentRgb24.HasValue) { @@ -918,7 +913,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable alpha[1] = rgb.R; alpha[3] = rgb.G; alpha[5] = rgb.B; - this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6); + this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer.Span, 0, 6); } } else if (pngMetadata.ColorType == PngColorType.Grayscale) @@ -926,13 +921,13 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable if (pngMetadata.TransparentL16.HasValue && this.use16Bit) { BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetadata.TransparentL16.Value.PackedValue); - this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2); + this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer.Span, 0, 2); } else if (pngMetadata.TransparentL8.HasValue) { alpha.Clear(); alpha[1] = pngMetadata.TransparentL8.Value.PackedValue; - this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2); + this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer.Span, 0, 2); } } } @@ -1173,12 +1168,14 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable /// The of the data to write. private void WriteChunk(Stream stream, PngChunkType type, Span data, int offset, int length) { - BinaryPrimitives.WriteInt32BigEndian(this.buffer, length); - BinaryPrimitives.WriteUInt32BigEndian(this.buffer.AsSpan(4, 4), (uint)type); + Span buffer = stackalloc byte[8]; + + BinaryPrimitives.WriteInt32BigEndian(buffer, length); + BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(4, 4), (uint)type); - stream.Write(this.buffer, 0, 8); + stream.Write(buffer); - uint crc = Crc32.Calculate(this.buffer.AsSpan(4, 4)); // Write the type buffer + uint crc = Crc32.Calculate(buffer.Slice(4)); // Write the type buffer if (data.Length > 0 && length > 0) { @@ -1187,9 +1184,9 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable crc = Crc32.Calculate(crc, data.Slice(offset, length)); } - BinaryPrimitives.WriteUInt32BigEndian(this.buffer, crc); + BinaryPrimitives.WriteUInt32BigEndian(buffer, crc); - stream.Write(this.buffer, 0, 4); // write the crc + stream.Write(buffer, 0, 4); // write the crc } /// @@ -1412,4 +1409,12 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable Type t when t == typeof(RgbaVector) => PngBitDepth.Bit16, _ => PngBitDepth.Bit8 }; + + private unsafe struct ScratchBuffer + { + private const int Size = 16; + private fixed byte scratch[Size]; + + public Span Span => MemoryMarshal.CreateSpan(ref this.scratch[0], Size); + } } diff --git a/src/ImageSharp/Formats/Png/PngEncoderHelpers.cs b/src/ImageSharp/Formats/Png/PngEncoderHelpers.cs index 5bee1ebd53..712bd0e06a 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderHelpers.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderHelpers.cs @@ -31,13 +31,13 @@ internal static class PngEncoderHelpers for (int i = 0; i < source.Length; i++) { - int value = ((int)MathF.Round(Unsafe.Add(ref sourceRef, i) / scale)) & mask; + int value = ((int)MathF.Round(Unsafe.Add(ref sourceRef, (uint)i) / scale)) & mask; v |= value << shift; if (shift == 0) { shift = shift0; - Unsafe.Add(ref resultRef, resultOffset) = (byte)v; + Unsafe.Add(ref resultRef, (uint)resultOffset) = (byte)v; resultOffset++; v = 0; } @@ -49,7 +49,7 @@ internal static class PngEncoderHelpers if (shift != shift0) { - Unsafe.Add(ref resultRef, resultOffset) = (byte)v; + Unsafe.Add(ref resultRef, (uint)resultOffset) = (byte)v; } } } diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs index cf77788bc0..04a23308cc 100644 --- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs +++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs @@ -32,7 +32,8 @@ internal static class PngScanlineProcessor { if (header.BitDepth == 16) { - for (int x = 0, o = 0; x < header.Width; x++, o += 2) + int o = 0; + for (nuint x = 0; x < (uint)header.Width; x++, o += 2) { ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); pixel.FromL16(Unsafe.As(ref luminance)); @@ -41,7 +42,7 @@ internal static class PngScanlineProcessor } else { - for (int x = 0; x < header.Width; x++) + for (nuint x = 0; x < (uint)header.Width; x++) { byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor); pixel.FromL8(Unsafe.As(ref luminance)); @@ -55,7 +56,8 @@ internal static class PngScanlineProcessor if (header.BitDepth == 16) { La32 source = default; - for (int x = 0, o = 0; x < header.Width; x++, o += 2) + int o = 0; + for (nuint x = 0; x < (uint)header.Width; x++, o += 2) { ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); source.L = luminance; @@ -69,7 +71,7 @@ internal static class PngScanlineProcessor { La16 source = default; byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor); - for (int x = 0; x < header.Width; x++) + for (nuint x = 0; x < (uint)header.Width; x++) { byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor); source.L = luminance; @@ -85,8 +87,8 @@ internal static class PngScanlineProcessor in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, - int pixelOffset, - int increment, + uint pixelOffset, + uint increment, bool hasTrans, L16 luminance16Trans, L8 luminanceTrans) @@ -101,7 +103,8 @@ internal static class PngScanlineProcessor { if (header.BitDepth == 16) { - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 2) + int o = 0; + for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += 2) { ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); pixel.FromL16(Unsafe.As(ref luminance)); @@ -110,7 +113,7 @@ internal static class PngScanlineProcessor } else { - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++) + for (nuint x = pixelOffset, o = 0; x < (uint)header.Width; x += increment, o++) { byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor); pixel.FromL8(Unsafe.As(ref luminance)); @@ -124,7 +127,8 @@ internal static class PngScanlineProcessor if (header.BitDepth == 16) { La32 source = default; - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 2) + int o = 0; + for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += 2) { ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); source.L = luminance; @@ -138,7 +142,7 @@ internal static class PngScanlineProcessor { La16 source = default; byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor); - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++) + for (nuint x = pixelOffset, o = 0; x < (uint)header.Width; x += increment, o++) { byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor); source.L = luminance; @@ -154,8 +158,8 @@ internal static class PngScanlineProcessor in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, - int bytesPerPixel, - int bytesPerSample) + uint bytesPerPixel, + uint bytesPerSample) where TPixel : unmanaged, IPixel { TPixel pixel = default; @@ -165,7 +169,8 @@ internal static class PngScanlineProcessor if (header.BitDepth == 16) { La32 source = default; - for (int x = 0, o = 0; x < header.Width; x++, o += 4) + int o = 0; + for (nuint x = 0; x < (uint)header.Width; x++, o += 4) { source.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); @@ -177,9 +182,9 @@ internal static class PngScanlineProcessor else { La16 source = default; - for (int x = 0; x < header.Width; x++) + for (nuint x = 0; x < (uint)header.Width; x++) { - int offset = x * bytesPerPixel; + nuint offset = x * bytesPerPixel; source.L = Unsafe.Add(ref scanlineSpanRef, offset); source.A = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample); @@ -193,10 +198,10 @@ internal static class PngScanlineProcessor in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, - int pixelOffset, - int increment, - int bytesPerPixel, - int bytesPerSample) + uint pixelOffset, + uint increment, + uint bytesPerPixel, + uint bytesPerSample) where TPixel : unmanaged, IPixel { TPixel pixel = default; @@ -206,20 +211,21 @@ internal static class PngScanlineProcessor if (header.BitDepth == 16) { La32 source = default; - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 4) + int o = 0; + for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += 4) { source.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); pixel.FromLa32(source); - Unsafe.Add(ref rowSpanRef, x) = pixel; + Unsafe.Add(ref rowSpanRef, (uint)x) = pixel; } } else { - int offset = 0; La16 source = default; - for (int x = pixelOffset; x < header.Width; x += increment) + nuint offset = 0; + for (nuint x = pixelOffset; x < (uint)header.Width; x += increment) { source.L = Unsafe.Add(ref scanlineSpanRef, offset); source.A = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample); @@ -255,11 +261,11 @@ internal static class PngScanlineProcessor // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha // channel and we should try to read it. Rgba32 rgba = default; - ref byte paletteAlphaRef = ref paletteAlpha[0]; + ref byte paletteAlphaRef = ref MemoryMarshal.GetArrayDataReference(paletteAlpha); - for (int x = 0; x < header.Width; x++) + for (nuint x = 0; x < (uint)header.Width; x++) { - int index = Unsafe.Add(ref scanlineSpanRef, x); + uint index = Unsafe.Add(ref scanlineSpanRef, x); rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index); rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue; @@ -269,7 +275,7 @@ internal static class PngScanlineProcessor } else { - for (int x = 0; x < header.Width; x++) + for (nuint x = 0; x < (uint)header.Width; x++) { int index = Unsafe.Add(ref scanlineSpanRef, x); Rgb24 rgb = Unsafe.Add(ref palettePixelsRef, index); @@ -284,8 +290,8 @@ internal static class PngScanlineProcessor in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, - int pixelOffset, - int increment, + uint pixelOffset, + uint increment, ReadOnlySpan palette, byte[] paletteAlpha) where TPixel : unmanaged, IPixel @@ -301,10 +307,10 @@ internal static class PngScanlineProcessor // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha // channel and we should try to read it. Rgba32 rgba = default; - ref byte paletteAlphaRef = ref paletteAlpha[0]; - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++) + ref byte paletteAlphaRef = ref MemoryMarshal.GetArrayDataReference(paletteAlpha); + for (nuint x = pixelOffset, o = 0; x < (uint)header.Width; x += increment, o++) { - int index = Unsafe.Add(ref scanlineSpanRef, o); + uint index = Unsafe.Add(ref scanlineSpanRef, o); rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue; rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index); @@ -314,7 +320,7 @@ internal static class PngScanlineProcessor } else { - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++) + for (nuint x = pixelOffset, o = 0; x < (uint)header.Width; x += increment, o++) { int index = Unsafe.Add(ref scanlineSpanRef, o); Rgb24 rgb = Unsafe.Add(ref palettePixelsRef, index); @@ -345,7 +351,8 @@ internal static class PngScanlineProcessor if (header.BitDepth == 16) { Rgb48 rgb48 = default; - for (int x = 0, o = 0; x < header.Width; x++, o += bytesPerPixel) + int o = 0; + for (nuint x = 0; x < (uint)header.Width; x++, o += bytesPerPixel) { rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); @@ -367,7 +374,8 @@ internal static class PngScanlineProcessor { Rgb48 rgb48 = default; Rgba64 rgba64 = default; - for (int x = 0, o = 0; x < header.Width; x++, o += bytesPerPixel) + int o = 0; + for (nuint x = 0; x < (uint)header.Width; x++, o += bytesPerPixel) { rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); @@ -385,7 +393,7 @@ internal static class PngScanlineProcessor Rgba32 rgba32 = default; ReadOnlySpan rgb24Span = MemoryMarshal.Cast(scanlineSpan); ref Rgb24 rgb24SpanRef = ref MemoryMarshal.GetReference(rgb24Span); - for (int x = 0; x < header.Width; x++) + for (nuint x = 0; x < (uint)header.Width; x++) { ref readonly Rgb24 rgb24 = ref Unsafe.Add(ref rgb24SpanRef, x); rgba32.Rgb = rgb24; @@ -401,8 +409,8 @@ internal static class PngScanlineProcessor in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, - int pixelOffset, - int increment, + uint pixelOffset, + uint increment, int bytesPerPixel, int bytesPerSample, bool hasTrans, @@ -420,7 +428,8 @@ internal static class PngScanlineProcessor { Rgb48 rgb48 = default; Rgba64 rgba64 = default; - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) + int o = 0; + for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += bytesPerPixel) { rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); @@ -436,7 +445,8 @@ internal static class PngScanlineProcessor else { Rgb48 rgb48 = default; - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) + int o = 0; + for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += bytesPerPixel) { rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); @@ -453,11 +463,12 @@ internal static class PngScanlineProcessor if (hasTrans) { Rgba32 rgba = default; - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) + int o = 0; + for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += bytesPerPixel) { - rgba.R = Unsafe.Add(ref scanlineSpanRef, o); - rgba.G = Unsafe.Add(ref scanlineSpanRef, o + bytesPerSample); - rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample)); + rgba.R = Unsafe.Add(ref scanlineSpanRef, (uint)o); + rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); + rgba.B = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); rgba.A = rgb24Trans.Equals(rgba.Rgb) ? byte.MinValue : byte.MaxValue; pixel.FromRgba32(rgba); @@ -467,11 +478,12 @@ internal static class PngScanlineProcessor else { Rgb24 rgb = default; - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) + int o = 0; + for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += bytesPerPixel) { - rgb.R = Unsafe.Add(ref scanlineSpanRef, o); - rgb.G = Unsafe.Add(ref scanlineSpanRef, o + bytesPerSample); - rgb.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample)); + rgb.R = Unsafe.Add(ref scanlineSpanRef, (uint)o); + rgb.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); + rgb.B = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); pixel.FromRgb24(rgb); Unsafe.Add(ref rowSpanRef, x) = pixel; @@ -494,7 +506,8 @@ internal static class PngScanlineProcessor if (header.BitDepth == 16) { Rgba64 rgba64 = default; - for (int x = 0, o = 0; x < header.Width; x++, o += bytesPerPixel) + int o = 0; + for (nuint x = 0; x < (uint)header.Width; x++, o += bytesPerPixel) { rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); @@ -515,8 +528,8 @@ internal static class PngScanlineProcessor in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, - int pixelOffset, - int increment, + uint pixelOffset, + uint increment, int bytesPerPixel, int bytesPerSample) where TPixel : unmanaged, IPixel @@ -528,7 +541,8 @@ internal static class PngScanlineProcessor if (header.BitDepth == 16) { Rgba64 rgba64 = default; - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) + int o = 0; + for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += bytesPerPixel) { rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); @@ -542,12 +556,13 @@ internal static class PngScanlineProcessor else { Rgba32 rgba = default; - for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) + int o = 0; + for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += bytesPerPixel) { - rgba.R = Unsafe.Add(ref scanlineSpanRef, o); - rgba.G = Unsafe.Add(ref scanlineSpanRef, o + bytesPerSample); - rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample)); - rgba.A = Unsafe.Add(ref scanlineSpanRef, o + (3 * bytesPerSample)); + rgba.R = Unsafe.Add(ref scanlineSpanRef, (uint)o); + rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); + rgba.B = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (2 * bytesPerSample))); + rgba.A = Unsafe.Add(ref scanlineSpanRef, (uint)(o + (3 * bytesPerSample))); pixel.FromRgba32(rgba); Unsafe.Add(ref rowSpanRef, x) = pixel; diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 2428763432..26e057bff9 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -16,11 +17,6 @@ namespace SixLabors.ImageSharp.Formats.Tga; /// internal sealed class TgaDecoderCore : IImageDecoderInternals { - /// - /// A scratch buffer to reduce allocations. - /// - private readonly byte[] scratchBuffer = new byte[4]; - /// /// General configuration options. /// @@ -406,6 +402,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals bool invertX = InvertX(origin); using IMemoryOwner row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0); Span rowSpan = row.GetSpan(); + Span scratchBuffer = stackalloc byte[2]; for (int y = 0; y < height; y++) { @@ -416,7 +413,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - int bytesRead = stream.Read(this.scratchBuffer, 0, 2); + int bytesRead = stream.Read(scratchBuffer); if (bytesRead != 2) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); @@ -424,16 +421,16 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals if (!this.hasAlpha) { - this.scratchBuffer[1] |= 1 << 7; + scratchBuffer[1] |= 1 << 7; } if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite) { - color.FromLa16(Unsafe.As(ref this.scratchBuffer[0])); + color.FromLa16(Unsafe.As(ref MemoryMarshal.GetReference(scratchBuffer))); } else { - color.FromBgra5551(Unsafe.As(ref this.scratchBuffer[0])); + color.FromBgra5551(Unsafe.As(ref MemoryMarshal.GetReference(scratchBuffer))); } pixelSpan[x] = color; @@ -483,6 +480,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals bool invertX = InvertX(origin); if (invertX) { + Span scratchBuffer = stackalloc byte[4]; TPixel color = default; for (int y = 0; y < height; y++) { @@ -490,7 +488,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals Span pixelSpan = pixels.DangerousGetRowSpan(newY); for (int x = width - 1; x >= 0; x--) { - this.ReadBgr24Pixel(stream, color, x, pixelSpan); + ReadBgr24Pixel(stream, color, x, pixelSpan, scratchBuffer); } } @@ -557,6 +555,8 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals return; } + Span scratchBuffer = stackalloc byte[4]; + for (int y = 0; y < height; y++) { int newY = InvertY(y, height, origin); @@ -565,14 +565,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - this.ReadBgra32Pixel(stream, x, color, pixelRow); + this.ReadBgra32Pixel(stream, x, color, pixelRow, scratchBuffer); } } else { for (int x = 0; x < width; x++) { - this.ReadBgra32Pixel(stream, x, color, pixelRow); + this.ReadBgra32Pixel(stream, x, color, pixelRow, scratchBuffer); } } } @@ -658,8 +658,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals this.ReadFileHeader(stream); return new ImageInfo( new PixelTypeInfo(this.fileHeader.PixelDepth), - this.fileHeader.Width, - this.fileHeader.Height, + new(this.fileHeader.Width, this.fileHeader.Height), this.metadata); } @@ -687,16 +686,16 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadBgr24Pixel(BufferedReadStream stream, TPixel color, int x, Span pixelSpan) + private static void ReadBgr24Pixel(BufferedReadStream stream, TPixel color, int x, Span pixelSpan, Span scratchBuffer) where TPixel : unmanaged, IPixel { - int bytesRead = stream.Read(this.scratchBuffer, 0, 3); + int bytesRead = stream.Read(scratchBuffer, 0, 3); if (bytesRead != 3) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgr pixel"); } - color.FromBgr24(Unsafe.As(ref this.scratchBuffer[0])); + color.FromBgr24(Unsafe.As(ref MemoryMarshal.GetReference(scratchBuffer))); pixelSpan[x] = color; } @@ -715,10 +714,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadBgra32Pixel(BufferedReadStream stream, int x, TPixel color, Span pixelRow) + private void ReadBgra32Pixel(BufferedReadStream stream, int x, TPixel color, Span pixelRow, Span scratchBuffer) where TPixel : unmanaged, IPixel { - int bytesRead = stream.Read(this.scratchBuffer, 0, 4); + int bytesRead = stream.Read(scratchBuffer, 0, 4); if (bytesRead != 4) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgra pixel"); @@ -726,8 +725,8 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals Guard.NotNull(this.tgaMetadata); - byte alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : this.scratchBuffer[3]; - color.FromBgra32(new Bgra32(this.scratchBuffer[2], this.scratchBuffer[1], this.scratchBuffer[0], alpha)); + byte alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : scratchBuffer[3]; + color.FromBgra32(new Bgra32(scratchBuffer[2], scratchBuffer[1], scratchBuffer[0], alpha)); pixelRow[x] = color; } @@ -814,7 +813,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals private void UncompressRle(BufferedReadStream stream, int width, int height, Span buffer, int bytesPerPixel) { int uncompressedPixels = 0; - Span pixel = this.scratchBuffer.AsSpan(0, bytesPerPixel); + Span pixel = stackalloc byte[bytesPerPixel]; int totalPixels = width * height; while (uncompressedPixels < totalPixels) { @@ -825,7 +824,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals if (highBit == 1) { int runLength = runLengthByte & 127; - int bytesRead = stream.Read(pixel, 0, bytesPerPixel); + int bytesRead = stream.Read(pixel); if (bytesRead != bytesPerPixel) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream"); @@ -845,7 +844,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals int bufferIdx = uncompressedPixels * bytesPerPixel; for (int i = 0; i < runLength + 1; i++, uncompressedPixels++) { - int bytesRead = stream.Read(pixel, 0, bytesPerPixel); + int bytesRead = stream.Read(pixel); if (bytesRead != bytesPerPixel) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream"); diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs index f468ab9ae7..ad63bd356d 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs @@ -22,11 +22,6 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// private readonly MemoryAllocator memoryAllocator; - /// - /// Reusable buffer for writing data. - /// - private readonly byte[] buffer = new byte[2]; - /// /// The color depth, in number of bits per pixel. /// @@ -221,9 +216,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals case TgaBitsPerPixel.Pixel16: Bgra5551 bgra5551 = new(color.ToVector4()); - BinaryPrimitives.WriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue); - stream.WriteByte(this.buffer[0]); - stream.WriteByte(this.buffer[1]); + Span buffer = stackalloc byte[2]; + BinaryPrimitives.WriteInt16LittleEndian(buffer, (short)bgra5551.PackedValue); + stream.WriteByte(buffer[0]); + stream.WriteByte(buffer[1]); break; diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index eb97272cb8..c868fec626 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -78,7 +78,7 @@ internal sealed class T6TiffCompression : TiffBaseDecompressor nint bitPos = Numerics.Modulo8(bitsWritten); nint bufferPos = bitsWritten / 8; ref byte scanLineRef = ref MemoryMarshal.GetReference(scanLine); - for (nint i = 0; i < scanLine.Length; i++) + for (nuint i = 0; i < (uint)scanLine.Length; i++) { if (Unsafe.Add(ref scanLineRef, i) != this.white) { diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffInkSet.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffInkSet.cs new file mode 100644 index 0000000000..abdaca8900 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffInkSet.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Metadata.Profiles.Exif; + +namespace SixLabors.ImageSharp.Formats.Tiff.Constants; + +/// +/// Enumeration representing the set of inks used in a separated () image. +/// +public enum TiffInkSet : ushort +{ + /// + /// CMYK. + /// The order of the components is cyan, magenta, yellow, black. + /// Usually, a value of 0 represents 0% ink coverage and a value of 255 represents 100% ink coverage for that component, but see DotRange. + /// The field should not exist when InkSet=1. + /// + Cmyk = 1, + + /// + /// Not CMYK. + /// See the field for a description of the inks to be used. + /// + NotCmyk = 2 +} diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs index cb537dda3c..6585be6f2f 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffPhotometricInterpretation.cs @@ -9,9 +9,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants; public enum TiffPhotometricInterpretation : ushort { /// - /// Bilevel and grayscale: 0 is imaged as white. The maximum value is imaged as black. - /// - /// Not supported by the TiffEncoder. + /// Bilevel and grayscale: 0 is imaged as white. The maximum value is imaged as black. + /// Not supported by the TiffEncoder. /// WhiteIsZero = 0, @@ -31,58 +30,50 @@ public enum TiffPhotometricInterpretation : ushort PaletteColor = 3, /// - /// A transparency mask. - /// - /// Not supported by the TiffEncoder. + /// A transparency mask. + /// Not supported by the TiffEncoder. /// TransparencyMask = 4, /// - /// Separated: usually CMYK (see Section 16 of the TIFF 6.0 specification). - /// - /// Not supported by the TiffEncoder. + /// Separated: usually CMYK (see Section 16 of the TIFF 6.0 specification). + /// Not supported by the TiffEncoder. /// Separated = 5, /// - /// YCbCr (see Section 21 of the TIFF 6.0 specification). - /// - /// Not supported by the TiffEncoder. + /// YCbCr (see Section 21 of the TIFF 6.0 specification). + /// Not supported by the TiffEncoder. /// YCbCr = 6, /// - /// 1976 CIE L*a*b* (see Section 23 of the TIFF 6.0 specification). - /// - /// Not supported by the TiffEncoder. + /// 1976 CIE L*a*b* (see Section 23 of the TIFF 6.0 specification). + /// Not supported by the TiffEncoder. /// CieLab = 8, /// - /// ICC L*a*b* (see TIFF Specification, supplement 1). - /// - /// Not supported by the TiffEncoder. + /// ICC L*a*b* (see TIFF Specification, supplement 1). + /// Not supported by the TiffEncoder. /// IccLab = 9, /// - /// ITU L*a*b* (see RFC2301). - /// - /// Not supported by the TiffEncoder. + /// ITU L*a*b* (see RFC2301). + /// Not supported by the TiffEncoder. /// ItuLab = 10, /// - /// Color Filter Array (see the DNG specification). - /// - /// Not supported by the TiffEncoder. + /// Color Filter Array (see the DNG specification). + /// Not supported by the TiffEncoder. /// ColorFilterArray = 32803, /// - /// Linear Raw (see the DNG specification). - /// - /// Not supported by the TiffEncoder. + /// Linear Raw (see the DNG specification). + /// Not supported by the TiffEncoder. /// LinearRaw = 34892 } diff --git a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs index 54d5c4ce86..755e79e42e 100644 --- a/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs +++ b/src/ImageSharp/Formats/Tiff/Ifd/DirectoryReader.cs @@ -37,7 +37,7 @@ internal class DirectoryReader /// Reads image file directories. /// /// Image file directories. - public IEnumerable Read() + public IList Read() { this.ByteOrder = ReadByteOrder(this.stream); var headerReader = new HeaderReader(this.stream, this.ByteOrder); @@ -66,7 +66,7 @@ internal class DirectoryReader throw TiffThrowHelper.ThrowInvalidHeader(); } - private IEnumerable ReadIfds(bool isBigTiff) + private IList ReadIfds(bool isBigTiff) { var readers = new List(); while (this.nextIfdOffset != 0 && this.nextIfdOffset < (ulong)this.stream.Length) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs index 6187ab8faa..a8a70f7272 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs @@ -18,21 +18,21 @@ internal class BlackIsZero1TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - nint offset = 0; - var colorBlack = default(TPixel); - var colorWhite = default(TPixel); + nuint offset = 0; + TPixel colorBlack = default; + TPixel colorWhite = default; colorBlack.FromRgba32(Color.Black); colorWhite.FromRgba32(Color.White); ref byte dataRef = ref MemoryMarshal.GetReference(data); - for (nint y = top; y < top + height; y++) + for (nuint y = (uint)top; y < (uint)(top + height); y++) { Span pixelRowSpan = pixels.DangerousGetRowSpan((int)y); ref TPixel pixelRowRef = ref MemoryMarshal.GetReference(pixelRowSpan); - for (nint x = left; x < left + width; x += 8) + for (nuint x = (uint)left; x < (uint)(left + width); x += 8) { byte b = Unsafe.Add(ref dataRef, offset++); - nint maxShift = Math.Min(left + width - x, 8); + nuint maxShift = Math.Min((uint)(left + width) - x, 8); if (maxShift == 8) { @@ -70,9 +70,9 @@ internal class BlackIsZero1TiffColor : TiffBaseColorDecoder } else { - for (int shift = 0; shift < maxShift; shift++) + for (nuint shift = 0; shift < maxShift; shift++) { - int bit = (b >> (7 - shift)) & 1; + int bit = (b >> (7 - (int)shift)) & 1; ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); pixel = bit == 0 ? colorBlack : colorWhite; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs index 9007b3f5ab..df37327c35 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs @@ -24,9 +24,9 @@ internal class BlackIsZero32FloatTiffColor : TiffBaseColorDecoder public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); + TPixel color = default; color.FromScaledVector4(Vector4.Zero); - byte[] buffer = new byte[4]; + Span buffer = stackalloc byte[4]; int offset = 0; for (int y = top; y < top + height; y++) @@ -37,8 +37,8 @@ internal class BlackIsZero32FloatTiffColor : TiffBaseColorDecoder : TiffBaseColorDecoder : TiffBaseColorDecoder var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); int offset = 0; - byte[] buffer = new byte[4]; + Span buffer = stackalloc byte[4]; for (int y = top; y < top + height; y++) { @@ -38,18 +38,18 @@ internal class RgbFloat323232TiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float r = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float r = BitConverter.ToSingle(buffer); offset += 4; data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float g = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float g = BitConverter.ToSingle(buffer); offset += 4; data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float b = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float b = BitConverter.ToSingle(buffer); offset += 4; var colorVector = new Vector4(r, g, b, 1.0f); @@ -61,16 +61,13 @@ internal class RgbFloat323232TiffColor : TiffBaseColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - data.Slice(offset, 4).CopyTo(buffer); - float r = BitConverter.ToSingle(buffer, 0); + float r = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; - data.Slice(offset, 4).CopyTo(buffer); - float g = BitConverter.ToSingle(buffer, 0); + float g = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; - data.Slice(offset, 4).CopyTo(buffer); - float b = BitConverter.ToSingle(buffer, 0); + float b = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; var colorVector = new Vector4(r, g, b, 1.0f); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs index 920f9fdc43..743502d56e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs @@ -27,7 +27,7 @@ internal class RgbaFloat32323232TiffColor : TiffBaseColorDecoder var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); int offset = 0; - byte[] buffer = new byte[4]; + Span buffer = stackalloc byte[4]; for (int y = top; y < top + height; y++) { @@ -38,23 +38,23 @@ internal class RgbaFloat32323232TiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float r = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float r = BitConverter.ToSingle(buffer); offset += 4; data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float g = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float g = BitConverter.ToSingle(buffer); offset += 4; data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float b = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float b = BitConverter.ToSingle(buffer); offset += 4; data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float a = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float a = BitConverter.ToSingle(buffer); offset += 4; var colorVector = new Vector4(r, g, b, a); @@ -66,20 +66,16 @@ internal class RgbaFloat32323232TiffColor : TiffBaseColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - data.Slice(offset, 4).CopyTo(buffer); - float r = BitConverter.ToSingle(buffer, 0); + float r = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; - data.Slice(offset, 4).CopyTo(buffer); - float g = BitConverter.ToSingle(buffer, 0); + float g = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; - data.Slice(offset, 4).CopyTo(buffer); - float b = BitConverter.ToSingle(buffer, 0); + float b = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; - data.Slice(offset, 4).CopyTo(buffer); - float a = BitConverter.ToSingle(buffer, 0); + float a = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; var colorVector = new Vector4(r, g, b, a); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs index a7519bc144..c5b662979e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs @@ -17,21 +17,21 @@ internal class WhiteIsZero1TiffColor : TiffBaseColorDecoder /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - nint offset = 0; + nuint offset = 0; var colorBlack = default(TPixel); var colorWhite = default(TPixel); colorBlack.FromRgba32(Color.Black); colorWhite.FromRgba32(Color.White); ref byte dataRef = ref MemoryMarshal.GetReference(data); - for (nint y = top; y < top + height; y++) + for (nuint y = (uint)top; y < (uint)(top + height); y++) { Span pixelRowSpan = pixels.DangerousGetRowSpan((int)y); ref TPixel pixelRowRef = ref MemoryMarshal.GetReference(pixelRowSpan); - for (nint x = left; x < left + width; x += 8) + for (nuint x = (uint)left; x < (uint)(left + width); x += 8) { byte b = Unsafe.Add(ref dataRef, offset++); - nint maxShift = Math.Min(left + width - x, 8); + nuint maxShift = Math.Min((uint)(left + width) - x, 8); if (maxShift == 8) { @@ -69,9 +69,9 @@ internal class WhiteIsZero1TiffColor : TiffBaseColorDecoder } else { - for (int shift = 0; shift < maxShift; shift++) + for (nuint shift = 0; shift < maxShift; shift++) { - int bit = (b >> (7 - shift)) & 1; + int bit = (b >> (7 - (int)shift)) & 1; ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); pixel = bit == 0 ? colorWhite : colorBlack; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs index 78d557f30b..f3207b2f45 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs @@ -26,7 +26,7 @@ internal class WhiteIsZero32FloatTiffColor : TiffBaseColorDecoder buffer = stackalloc byte[4]; int offset = 0; for (int y = top; y < top + height; y++) @@ -37,8 +37,8 @@ internal class WhiteIsZero32FloatTiffColor : TiffBaseColorDecoder : TiffBaseColorDecoder - /// 6 bits per pixel. 2 bit for each color channel. - /// - /// Note: The TiffEncoder does not yet support 2 bits per color channel and will default to 24 bits per pixel instead. + /// 6 bits per pixel. 2 bit for each color channel. + /// Note: The TiffEncoder does not yet support 2 bits per color channel and will default to 24 bits per pixel instead. /// Bit6 = 6, @@ -31,30 +30,26 @@ public enum TiffBitsPerPixel Bit8 = 8, /// - /// 10 bits per pixel, for gray images. - /// - /// Note: The TiffEncoder does not yet support 10 bits per pixel and will default to 24 bits per pixel instead. + /// 10 bits per pixel, for gray images. + /// Note: The TiffEncoder does not yet support 10 bits per pixel and will default to 24 bits per pixel instead. /// Bit10 = 10, /// - /// 12 bits per pixel. 4 bit for each color channel. - /// - /// Note: The TiffEncoder does not yet support 4 bits per color channel and will default to 24 bits per pixel instead. + /// 12 bits per pixel. 4 bit for each color channel. + /// Note: The TiffEncoder does not yet support 4 bits per color channel and will default to 24 bits per pixel instead. /// Bit12 = 12, /// - /// 14 bits per pixel, for gray images. - /// - /// Note: The TiffEncoder does not yet support 14 bits per pixel images and will default to 24 bits per pixel instead. + /// 14 bits per pixel, for gray images. + /// Note: The TiffEncoder does not yet support 14 bits per pixel images and will default to 24 bits per pixel instead. /// Bit14 = 14, /// - /// 16 bits per pixel, for gray images. - /// - /// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 16 bits grayscale instead. + /// 16 bits per pixel, for gray images. + /// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 16 bits grayscale instead. /// Bit16 = 16, @@ -64,30 +59,37 @@ public enum TiffBitsPerPixel Bit24 = 24, /// - /// 30 bits per pixel. 10 bit for each color channel. - /// - /// Note: The TiffEncoder does not yet support 10 bits per color channel and will default to 24 bits per pixel instead. + /// 30 bits per pixel. 10 bit for each color channel. + /// Note: The TiffEncoder does not yet support 10 bits per color channel and will default to 24 bits per pixel instead. /// Bit30 = 30, /// - /// 36 bits per pixel. 12 bit for each color channel. - /// - /// Note: The TiffEncoder does not yet support 12 bits per color channel and will default to 24 bits per pixel instead. + /// 32 bits per pixel. One byte for each color channel. + /// + Bit32 = 32, + + /// + /// 36 bits per pixel. 12 bit for each color channel. + /// Note: The TiffEncoder does not yet support 12 bits per color channel and will default to 24 bits per pixel instead. /// Bit36 = 36, /// - /// 42 bits per pixel. 14 bit for each color channel. - /// - /// Note: The TiffEncoder does not yet support 14 bits per color channel and will default to 24 bits per pixel instead. + /// 42 bits per pixel. 14 bit for each color channel. + /// Note: The TiffEncoder does not yet support 14 bits per color channel and will default to 24 bits per pixel instead. /// Bit42 = 42, /// - /// 48 bits per pixel. 16 bit for each color channel. - /// - /// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 24 bits per pixel instead. + /// 48 bits per pixel. 16 bit for each color channel. + /// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 24 bits per pixel instead. /// Bit48 = 48, + + /// + /// 64 bits per pixel. 16 bit for each color channel. + /// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 32 bits per pixel instead. + /// + Bit64 = 64, } diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 8333dbf31c..45bbed12d5 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -163,13 +163,14 @@ internal class TiffDecoderCore : IImageDecoderInternals public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - var frames = new List>(); + List> frames = new(); + List framesMetadata = new(); try { this.inputStream = stream; - var reader = new DirectoryReader(stream, this.configuration.MemoryAllocator); + DirectoryReader reader = new(stream, this.configuration.MemoryAllocator); - IEnumerable directories = reader.Read(); + IList directories = reader.Read(); this.byteOrder = reader.ByteOrder; this.isBigTiff = reader.IsBigTiff; @@ -179,6 +180,7 @@ internal class TiffDecoderCore : IImageDecoderInternals cancellationToken.ThrowIfCancellationRequested(); ImageFrame frame = this.DecodeFrame(ifd, cancellationToken); frames.Add(frame); + framesMetadata.Add(frame.Metadata); if (++frameCount == this.maxFrames) { @@ -186,7 +188,7 @@ internal class TiffDecoderCore : IImageDecoderInternals } } - ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, this.skipMetadata, reader.ByteOrder, reader.IsBigTiff); + ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, this.skipMetadata, reader.ByteOrder, reader.IsBigTiff); // TODO: Tiff frames can have different sizes. ImageFrame root = frames[0]; @@ -217,16 +219,22 @@ internal class TiffDecoderCore : IImageDecoderInternals { this.inputStream = stream; DirectoryReader reader = new(stream, this.configuration.MemoryAllocator); - IEnumerable directories = reader.Read(); + IList directories = reader.Read(); - ExifProfile rootFrameExifProfile = directories.First(); - TiffFrameMetadata rootMetadata = TiffFrameMetadata.Parse(rootFrameExifProfile); + List framesMetadata = new(); + foreach (ExifProfile dir in directories) + { + framesMetadata.Add(this.CreateFrameMetadata(dir)); + } + + ExifProfile rootFrameExifProfile = directories[0]; + + ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, this.skipMetadata, reader.ByteOrder, reader.IsBigTiff); - ImageMetadata metadata = TiffDecoderMetadataCreator.Create(reader.ByteOrder, reader.IsBigTiff, rootFrameExifProfile); int width = GetImageWidth(rootFrameExifProfile); int height = GetImageHeight(rootFrameExifProfile); - return new ImageInfo(new PixelTypeInfo((int)rootMetadata.BitsPerPixel), width, height, metadata); + return new ImageInfo(new PixelTypeInfo((int)framesMetadata[0].GetTiffMetadata().BitsPerPixel), new(width, height), metadata, framesMetadata); } /// @@ -239,16 +247,8 @@ internal class TiffDecoderCore : IImageDecoderInternals private ImageFrame DecodeFrame(ExifProfile tags, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - ImageFrameMetadata imageFrameMetaData = new(); - if (!this.skipMetadata) - { - imageFrameMetaData.ExifProfile = tags; - } - - TiffFrameMetadata tiffFrameMetaData = imageFrameMetaData.GetTiffMetadata(); - TiffFrameMetadata.Parse(tiffFrameMetaData, tags); - - bool isTiled = this.VerifyAndParse(tags, tiffFrameMetaData); + ImageFrameMetadata imageFrameMetaData = this.CreateFrameMetadata(tags); + bool isTiled = this.VerifyAndParse(tags, imageFrameMetaData.GetTiffMetadata()); int width = GetImageWidth(tags); int height = GetImageHeight(tags); @@ -266,6 +266,19 @@ internal class TiffDecoderCore : IImageDecoderInternals return frame; } + private ImageFrameMetadata CreateFrameMetadata(ExifProfile tags) + { + ImageFrameMetadata imageFrameMetaData = new(); + if (!this.skipMetadata) + { + imageFrameMetaData.ExifProfile = tags; + } + + TiffFrameMetadata.Parse(imageFrameMetaData.GetTiffMetadata(), tags); + + return imageFrameMetaData; + } + /// /// Decodes the image data for Tiff's which arrange the pixel data in stripes. /// @@ -772,7 +785,7 @@ internal class TiffDecoderCore : IImageDecoderInternals bitsPerPixel = this.BitsPerSample.Channel2; break; case 3: - bitsPerPixel = this.BitsPerSample.Channel2; + bitsPerPixel = this.BitsPerSample.Channel3; break; default: TiffThrowHelper.ThrowNotSupported("More then 4 color channels are not supported"); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs index 1a6c9dd7de..1ef2478e3d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs @@ -8,7 +8,6 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Iptc; using SixLabors.ImageSharp.Metadata.Profiles.Xmp; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Tiff; @@ -17,22 +16,20 @@ namespace SixLabors.ImageSharp.Formats.Tiff; /// internal static class TiffDecoderMetadataCreator { - public static ImageMetadata Create(List> frames, bool ignoreMetadata, ByteOrder byteOrder, bool isBigTiff) - where TPixel : unmanaged, IPixel + public static ImageMetadata Create(List frames, bool ignoreMetadata, ByteOrder byteOrder, bool isBigTiff) { if (frames.Count < 1) { TiffThrowHelper.ThrowImageFormatException("Expected at least one frame."); } - ImageMetadata imageMetaData = Create(byteOrder, isBigTiff, frames[0].Metadata.ExifProfile); + ImageMetadata imageMetaData = Create(byteOrder, isBigTiff, frames[0].ExifProfile); if (!ignoreMetadata) { for (int i = 0; i < frames.Count; i++) { - ImageFrame frame = frames[i]; - ImageFrameMetadata frameMetaData = frame.Metadata; + ImageFrameMetadata frameMetaData = frames[i]; if (TryGetIptc(frameMetaData.ExifProfile.Values, out byte[] iptcBytes)) { frameMetaData.IptcProfile = new IptcProfile(iptcBytes); @@ -53,15 +50,14 @@ internal static class TiffDecoderMetadataCreator return imageMetaData; } - public static ImageMetadata Create(ByteOrder byteOrder, bool isBigTiff, ExifProfile exifProfile) + private static ImageMetadata Create(ByteOrder byteOrder, bool isBigTiff, ExifProfile exifProfile) { - var imageMetaData = new ImageMetadata(); + ImageMetadata imageMetaData = new(); SetResolution(imageMetaData, exifProfile); TiffMetadata tiffMetadata = imageMetaData.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; tiffMetadata.FormatType = isBigTiff ? TiffFormatType.BigTIFF : TiffFormatType.Default; - return imageMetaData; } @@ -92,7 +88,7 @@ internal static class TiffDecoderMetadataCreator if (iptc != null) { - if (iptc.DataType == ExifDataType.Byte || iptc.DataType == ExifDataType.Undefined) + if (iptc.DataType is ExifDataType.Byte or ExifDataType.Undefined) { iptcBytes = (byte[])iptc.GetValue(); return true; diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index a13fb58de7..26905965ee 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -509,6 +509,11 @@ internal static class TiffDecoderOptionsParser TiffThrowHelper.ThrowNotSupported("Only 8 bits per channel is supported for CMYK images."); } + if (exifProfile.GetValueInternal(ExifTag.InkNames) is not null) + { + TiffThrowHelper.ThrowNotSupported("The custom ink name strings are not supported for CMYK images."); + } + options.ColorType = TiffColorType.Cmyk; break; } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 94c7fb2b1e..d7243c6964 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -29,11 +29,6 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals /// private readonly MemoryAllocator memoryAllocator; - /// - /// A scratch buffer to reduce allocations. - /// - private readonly byte[] buffer = new byte[4]; - /// /// The global configuration. /// @@ -157,7 +152,9 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals this.SanitizeAndSetEncoderOptions(bitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation, compression, predictor); using TiffStreamWriter writer = new(stream); - long ifdMarker = WriteHeader(writer); + Span buffer = stackalloc byte[4]; + + long ifdMarker = WriteHeader(writer, buffer); Image metadataImage = image; foreach (ImageFrame frame in image.Frames) @@ -171,7 +168,7 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals long currentOffset = writer.BaseStream.Position; foreach ((long, uint) marker in this.frameMarkers) { - writer.WriteMarkerFast(marker.Item1, marker.Item2); + writer.WriteMarkerFast(marker.Item1, marker.Item2, buffer); } writer.BaseStream.Seek(currentOffset, SeekOrigin.Begin); @@ -181,14 +178,15 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals /// Writes the TIFF file header. /// /// The to write data to. + /// Scratch buffer with minimum size of 2. /// /// The marker to write the first IFD offset. /// - public static long WriteHeader(TiffStreamWriter writer) + public static long WriteHeader(TiffStreamWriter writer, Span buffer) { - writer.Write(ByteOrderMarker); - writer.Write(TiffConstants.HeaderMagicNumber); - return writer.PlaceMarker(); + writer.Write(ByteOrderMarker, buffer); + writer.Write(TiffConstants.HeaderMagicNumber, buffer); + return writer.PlaceMarker(buffer); } /// @@ -307,20 +305,22 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals entries.Sort((a, b) => (ushort)a.Tag - (ushort)b.Tag); - writer.Write((ushort)entries.Count); + Span buffer = stackalloc byte[4]; + + writer.Write((ushort)entries.Count, buffer); foreach (IExifValue entry in entries) { - writer.Write((ushort)entry.Tag); - writer.Write((ushort)entry.DataType); - writer.Write(ExifWriter.GetNumberOfComponents(entry)); + writer.Write((ushort)entry.Tag, buffer); + writer.Write((ushort)entry.DataType, buffer); + writer.Write(ExifWriter.GetNumberOfComponents(entry), buffer); uint length = ExifWriter.GetLength(entry); if (length <= 4) { - int sz = ExifWriter.WriteValue(entry, this.buffer, 0); + int sz = ExifWriter.WriteValue(entry, buffer, 0); DebugGuard.IsTrue(sz == length, "Incorrect number of bytes written"); - writer.WritePadded(this.buffer.AsSpan(0, sz)); + writer.WritePadded(buffer.Slice(0, sz)); } else { @@ -328,12 +328,12 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals int sz = ExifWriter.WriteValue(entry, raw, 0); DebugGuard.IsTrue(sz == raw.Length, "Incorrect number of bytes written"); largeDataBlocks.Add(raw); - writer.Write(dataOffset); + writer.Write(dataOffset, buffer); dataOffset += (uint)(raw.Length + (raw.Length % 2)); } } - long nextIfdMarker = writer.PlaceMarker(); + long nextIfdMarker = writer.PlaceMarker(buffer); foreach (byte[] dataBlock in largeDataBlocks) { @@ -391,6 +391,10 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals // Encoding not yet supported bits per pixel will default to 24 bits. this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); break; + case TiffBitsPerPixel.Bit64: + // Encoding not yet supported bits per pixel will default to 32 bits. + this.SetEncoderOptions(TiffBitsPerPixel.Bit32, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); + break; default: this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.Rgb, compression, predictor); break; diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 5d0b85bf22..e309830984 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -28,6 +28,7 @@ public class TiffFrameMetadata : IDeepCloneable this.Compression = other.Compression; this.PhotometricInterpretation = other.PhotometricInterpretation; this.Predictor = other.Predictor; + this.InkSet = other.InkSet; } /// @@ -55,6 +56,11 @@ public class TiffFrameMetadata : IDeepCloneable /// public TiffPredictor? Predictor { get; set; } + /// + /// Gets or sets the set of inks used in a separated () image. + /// + public TiffInkSet? InkSet { get; set; } + /// /// Returns a new instance parsed from the given Exif profile. /// @@ -63,7 +69,7 @@ public class TiffFrameMetadata : IDeepCloneable /// The . internal static TiffFrameMetadata Parse(ExifProfile profile) { - var meta = new TiffFrameMetadata(); + TiffFrameMetadata meta = new(); Parse(meta, profile); return meta; } @@ -77,12 +83,10 @@ public class TiffFrameMetadata : IDeepCloneable { if (profile != null) { - if (profile.TryGetValue(ExifTag.BitsPerSample, out IExifValue? bitsPerSampleValue)) + if (profile.TryGetValue(ExifTag.BitsPerSample, out IExifValue? bitsPerSampleValue) + && TiffBitsPerSample.TryParse(bitsPerSampleValue.Value, out TiffBitsPerSample bitsPerSample)) { - if (TiffBitsPerSample.TryParse(bitsPerSampleValue.Value, out TiffBitsPerSample bitsPerSample)) - { - meta.BitsPerSample = bitsPerSample; - } + meta.BitsPerSample = bitsPerSample; } meta.BitsPerPixel = meta.BitsPerSample?.BitsPerPixel(); @@ -102,6 +106,11 @@ public class TiffFrameMetadata : IDeepCloneable meta.Predictor = (TiffPredictor)predictorValue.Value; } + if (profile.TryGetValue(ExifTag.InkSet, out IExifValue? inkSetValue)) + { + meta.InkSet = (TiffInkSet)inkSetValue.Value; + } + profile.RemoveValue(ExifTag.BitsPerSample); profile.RemoveValue(ExifTag.Compression); profile.RemoveValue(ExifTag.PhotometricInterpretation); diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs index 29ffc82ce0..2759d0130c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffMetadata.cs @@ -19,7 +19,11 @@ public class TiffMetadata : IDeepCloneable /// Initializes a new instance of the class. /// /// The metadata to create an instance from. - private TiffMetadata(TiffMetadata other) => this.ByteOrder = other.ByteOrder; + private TiffMetadata(TiffMetadata other) + { + this.ByteOrder = other.ByteOrder; + this.FormatType = other.FormatType; + } /// /// Gets or sets the byte order. diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter{TPixel}.cs index 189c8fd6ac..c4a7492553 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter{TPixel}.cs @@ -29,7 +29,7 @@ internal abstract class TiffBaseColorWriter : IDisposable /// /// Gets the bytes per row. /// - public int BytesPerRow => ((this.Image.Width * this.BitsPerPixel) + 7) / 8; + public int BytesPerRow => (int)(((uint)(this.Image.Width * this.BitsPerPixel) + 7) / 8); protected ImageFrame Image { get; } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs index be32ca9ed6..3c2ad60846 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs @@ -10,13 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers; /// internal sealed class TiffStreamWriter : IDisposable { - private static readonly byte[] PaddingBytes = new byte[4]; - - /// - /// A scratch buffer to reduce allocations. - /// - private readonly byte[] buffer = new byte[4]; - /// /// Initializes a new instance of the class. /// @@ -41,11 +34,12 @@ internal sealed class TiffStreamWriter : IDisposable /// /// Writes an empty four bytes to the stream, returning the offset to be written later. /// + /// Scratch buffer with minimum size of 4. /// The offset to be written later. - public long PlaceMarker() + public long PlaceMarker(Span buffer) { long offset = this.BaseStream.Position; - this.Write(0u); + this.Write(0u, buffer); return offset; } @@ -71,36 +65,38 @@ internal sealed class TiffStreamWriter : IDisposable /// Writes a two-byte unsigned integer to the current stream. /// /// The two-byte unsigned integer to write. - public void Write(ushort value) + /// Scratch buffer with minimum size of 2. + public void Write(ushort value, Span buffer) { if (IsLittleEndian) { - BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, value); + BinaryPrimitives.WriteUInt16LittleEndian(buffer, value); } else { - BinaryPrimitives.WriteUInt16BigEndian(this.buffer, value); + BinaryPrimitives.WriteUInt16BigEndian(buffer, value); } - this.BaseStream.Write(this.buffer.AsSpan(0, 2)); + this.BaseStream.Write(buffer.Slice(0, 2)); } /// /// Writes a four-byte unsigned integer to the current stream. /// /// The four-byte unsigned integer to write. - public void Write(uint value) + /// Scratch buffer with minimum size of 4. + public void Write(uint value, Span buffer) { if (IsLittleEndian) { - BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, value); + BinaryPrimitives.WriteUInt32LittleEndian(buffer, value); } else { - BinaryPrimitives.WriteUInt32BigEndian(this.buffer, value); + BinaryPrimitives.WriteUInt32BigEndian(buffer, value); } - this.BaseStream.Write(this.buffer.AsSpan(0, 4)); + this.BaseStream.Write(buffer.Slice(0, 4)); } /// @@ -113,7 +109,10 @@ internal sealed class TiffStreamWriter : IDisposable if (value.Length % 4 != 0) { - this.BaseStream.Write(PaddingBytes, 0, 4 - (value.Length % 4)); + // No allocation occurs, refers directly to assembly's data segment. + ReadOnlySpan paddingBytes = new byte[4] { 0x00, 0x00, 0x00, 0x00 }; + paddingBytes = paddingBytes[..(4 - (value.Length % 4))]; + this.BaseStream.Write(paddingBytes); } } @@ -122,18 +121,19 @@ internal sealed class TiffStreamWriter : IDisposable /// /// The offset returned when placing the marker /// The four-byte unsigned integer to write. - public void WriteMarker(long offset, uint value) + /// Scratch buffer. + public void WriteMarker(long offset, uint value, Span buffer) { long back = this.BaseStream.Position; this.BaseStream.Seek(offset, SeekOrigin.Begin); - this.Write(value); + this.Write(value, buffer); this.BaseStream.Seek(back, SeekOrigin.Begin); } - public void WriteMarkerFast(long offset, uint value) + public void WriteMarkerFast(long offset, uint value, Span buffer) { this.BaseStream.Seek(offset, SeekOrigin.Begin); - this.Write(value); + this.Write(value, buffer); } /// diff --git a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs index d45fa2a427..289ebd35ca 100644 --- a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs +++ b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -38,7 +38,7 @@ internal class AlphaDecoder : IDisposable this.LastRow = 0; int totalPixels = width * height; - var compression = (WebpAlphaCompressionMethod)(alphaChunkHeader & 0x03); + WebpAlphaCompressionMethod compression = (WebpAlphaCompressionMethod)(alphaChunkHeader & 0x03); if (compression is not WebpAlphaCompressionMethod.NoCompression and not WebpAlphaCompressionMethod.WebpLosslessCompression) { WebpThrowHelper.ThrowImageFormatException($"unexpected alpha compression method {compression} found"); @@ -59,7 +59,7 @@ internal class AlphaDecoder : IDisposable if (this.Compressed) { - var bitReader = new Vp8LBitReader(data); + Vp8LBitReader bitReader = new(data); this.LosslessDecoder = new WebpLosslessDecoder(bitReader, memoryAllocator, configuration); this.LosslessDecoder.DecodeImageStream(this.Vp8LDec, width, height, true); @@ -110,6 +110,7 @@ internal class AlphaDecoder : IDisposable /// /// Gets a value indicating whether the alpha channel uses compression. /// + [MemberNotNullWhen(true, nameof(LosslessDecoder))] private bool Compressed { get; } /// @@ -120,7 +121,7 @@ internal class AlphaDecoder : IDisposable /// /// Gets the Vp8L decoder which is used to de compress the alpha channel, if needed. /// - private WebpLosslessDecoder LosslessDecoder { get; } + private WebpLosslessDecoder? LosslessDecoder { get; } /// /// Gets a value indicating whether the decoding needs 1 byte per pixel for decoding. @@ -173,17 +174,14 @@ internal class AlphaDecoder : IDisposable dst = dst[this.Width..]; } } + else if (this.Use8BDecode) + { + this.LosslessDecoder.DecodeAlphaData(this); + } else { - if (this.Use8BDecode) - { - this.LosslessDecoder.DecodeAlphaData(this); - } - else - { - this.LosslessDecoder.DecodeImageData(this.Vp8LDec, this.Vp8LDec.Pixels.Memory.Span); - this.ExtractAlphaRows(this.Vp8LDec); - } + this.LosslessDecoder.DecodeImageData(this.Vp8LDec, this.Vp8LDec.Pixels.Memory.Span); + this.ExtractAlphaRows(this.Vp8LDec); } } @@ -261,8 +259,7 @@ internal class AlphaDecoder : IDisposable { int numRowsToProcess = dec.Height; int width = dec.Width; - Span pixels = dec.Pixels.Memory.Span; - Span input = pixels; + Span input = dec.Pixels.Memory.Span; Span output = this.Alpha.Memory.Span; // Extract alpha (which is stored in the green plane). @@ -322,12 +319,13 @@ internal class AlphaDecoder : IDisposable return; } - nint i; + nuint i; Vector128 last = Vector128.Zero.WithElement(0, dst[0]); ref byte srcRef = ref MemoryMarshal.GetReference(input); - for (i = 1; i + 8 <= width; i += 8) + ref byte dstRef = ref MemoryMarshal.GetReference(dst); + for (i = 1; i <= (uint)width - 8; i += 8) { - var a0 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref srcRef, i)), 0); + Vector128 a0 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref srcRef, i)), 0); Vector128 a1 = Sse2.Add(a0.AsByte(), last.AsByte()); Vector128 a2 = Sse2.ShiftLeftLogical128BitLane(a1, 1); Vector128 a3 = Sse2.Add(a1, a2); @@ -336,12 +334,12 @@ internal class AlphaDecoder : IDisposable Vector128 a6 = Sse2.ShiftLeftLogical128BitLane(a5, 4); Vector128 a7 = Sse2.Add(a5, a6); - ref byte outputRef = ref Unsafe.Add(ref MemoryMarshal.GetReference(dst), i); + ref byte outputRef = ref Unsafe.Add(ref dstRef, i); Unsafe.As>(ref outputRef) = a7.GetLower(); last = Sse2.ShiftRightLogical(a7.AsInt64(), 56).AsInt32(); } - for (; i < width; ++i) + for (; i < (uint)width; ++i) { dst[(int)i] = (byte)(input[(int)i] + dst[(int)i - 1]); } @@ -365,32 +363,33 @@ internal class AlphaDecoder : IDisposable { HorizontalUnfilter(null, input, dst, width); } - else + else if (Avx2.IsSupported) { - if (Avx2.IsSupported) + ref byte inputRef = ref MemoryMarshal.GetReference(input); + ref byte prevRef = ref MemoryMarshal.GetReference(prev); + ref byte dstRef = ref MemoryMarshal.GetReference(dst); + + nuint i; + int maxPos = width & ~31; + for (i = 0; i < (uint)maxPos; i += 32) { - nint i; - int maxPos = width & ~31; - for (i = 0; i < maxPos; i += 32) - { - Vector256 a0 = Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(input), i)); - Vector256 b0 = Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(prev), i)); - Vector256 c0 = Avx2.Add(a0.AsByte(), b0.AsByte()); - ref byte outputRef = ref Unsafe.Add(ref MemoryMarshal.GetReference(dst), i); - Unsafe.As>(ref outputRef) = c0; - } + Vector256 a0 = Unsafe.As>(ref Unsafe.Add(ref inputRef, i)); + Vector256 b0 = Unsafe.As>(ref Unsafe.Add(ref prevRef, i)); + Vector256 c0 = Avx2.Add(a0.AsByte(), b0.AsByte()); + ref byte outputRef = ref Unsafe.Add(ref dstRef, i); + Unsafe.As>(ref outputRef) = c0; + } - for (; i < width; i++) - { - dst[(int)i] = (byte)(prev[(int)i] + input[(int)i]); - } + for (; i < (uint)width; i++) + { + Unsafe.Add(ref dstRef, i) = (byte)(Unsafe.Add(ref prevRef, i) + Unsafe.Add(ref inputRef, i)); } - else + } + else + { + for (int i = 0; i < width; i++) { - for (int i = 0; i < width; i++) - { - dst[i] = (byte)(prev[i] + input[i]); - } + dst[i] = (byte)(prev[i] + input[i]); } } } diff --git a/src/ImageSharp/Formats/Webp/AlphaEncoder.cs b/src/ImageSharp/Formats/Webp/AlphaEncoder.cs index 292c31f6a5..596715b205 100644 --- a/src/ImageSharp/Formats/Webp/AlphaEncoder.cs +++ b/src/ImageSharp/Formats/Webp/AlphaEncoder.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using SixLabors.ImageSharp.Advanced; @@ -13,10 +12,8 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// /// Methods for encoding the alpha data of a VP8 image. /// -internal class AlphaEncoder : IDisposable +internal static class AlphaEncoder { - private IMemoryOwner alphaData; - /// /// Encodes the alpha channel data. /// Data is either compressed as lossless webp image or uncompressed. @@ -29,12 +26,18 @@ internal class AlphaEncoder : IDisposable /// Indicates, if the data should be compressed with the lossless webp compression. /// The size in bytes of the alpha data. /// The encoded alpha data. - public IMemoryOwner EncodeAlpha(Image image, Configuration configuration, MemoryAllocator memoryAllocator, bool skipMetadata, bool compress, out int size) + public static IMemoryOwner EncodeAlpha( + Image image, + Configuration configuration, + MemoryAllocator memoryAllocator, + bool skipMetadata, + bool compress, + out int size) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; - this.alphaData = ExtractAlphaChannel(image, configuration, memoryAllocator); + IMemoryOwner alphaData = ExtractAlphaChannel(image, configuration, memoryAllocator); if (compress) { @@ -55,15 +58,15 @@ internal class AlphaEncoder : IDisposable // The transparency information will be stored in the green channel of the ARGB quadruplet. // The green channel is allowed extra transformation steps in the specification -- unlike the other channels, // that can improve compression. - using Image alphaAsImage = DispatchAlphaToGreen(image, this.alphaData.GetSpan()); + using Image alphaAsImage = DispatchAlphaToGreen(image, alphaData.GetSpan()); - size = lossLessEncoder.EncodeAlphaImageData(alphaAsImage, this.alphaData); + size = lossLessEncoder.EncodeAlphaImageData(alphaAsImage, alphaData); - return this.alphaData; + return alphaData; } size = width * height; - return this.alphaData; + return alphaData; } /// @@ -128,7 +131,4 @@ internal class AlphaEncoder : IDisposable return alphaDataBuffer; } - - /// - public void Dispose() => this.alphaData?.Dispose(); } diff --git a/src/ImageSharp/Formats/Webp/BitReader/BitReaderBase.cs b/src/ImageSharp/Formats/Webp/BitReader/BitReaderBase.cs index 2586690fd3..83f9e797ab 100644 --- a/src/ImageSharp/Formats/Webp/BitReader/BitReaderBase.cs +++ b/src/ImageSharp/Formats/Webp/BitReader/BitReaderBase.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using SixLabors.ImageSharp.Memory; @@ -14,10 +13,16 @@ internal abstract class BitReaderBase : IDisposable { private bool isDisposed; + protected BitReaderBase(IMemoryOwner data) + => this.Data = data; + + protected BitReaderBase(Stream inputStream, int imageDataSize, MemoryAllocator memoryAllocator) + => this.Data = ReadImageDataFromStream(inputStream, imageDataSize, memoryAllocator); + /// - /// Gets or sets the raw encoded image data. + /// Gets the raw encoded image data. /// - public IMemoryOwner Data { get; set; } + public IMemoryOwner Data { get; } /// /// Copies the raw encoded image data from the stream into a byte array. @@ -25,11 +30,13 @@ internal abstract class BitReaderBase : IDisposable /// The input stream. /// Number of bytes to read as indicated from the chunk size. /// Used for allocating memory during reading data from the stream. - protected void ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator memoryAllocator) + protected static IMemoryOwner ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator memoryAllocator) { - this.Data = memoryAllocator.Allocate(bytesToRead); - Span dataSpan = this.Data.Memory.Span; + IMemoryOwner data = memoryAllocator.Allocate(bytesToRead); + Span dataSpan = data.Memory.Span; input.Read(dataSpan[..bytesToRead], 0, bytesToRead); + + return data; } protected virtual void Dispose(bool disposing) @@ -41,7 +48,7 @@ internal abstract class BitReaderBase : IDisposable if (disposing) { - this.Data?.Dispose(); + this.Data.Dispose(); } this.isDisposed = true; diff --git a/src/ImageSharp/Formats/Webp/BitReader/Vp8BitReader.cs b/src/ImageSharp/Formats/Webp/BitReader/Vp8BitReader.cs index 07bfcccd91..7b64d8329c 100644 --- a/src/ImageSharp/Formats/Webp/BitReader/Vp8BitReader.cs +++ b/src/ImageSharp/Formats/Webp/BitReader/Vp8BitReader.cs @@ -57,12 +57,12 @@ internal class Vp8BitReader : BitReaderBase /// The partition length. /// Start index in the data array. Defaults to 0. public Vp8BitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator, uint partitionLength, int startPos = 0) + : base(inputStream, (int)imageDataSize, memoryAllocator) { Guard.MustBeLessThan(imageDataSize, int.MaxValue, nameof(imageDataSize)); this.ImageDataSize = imageDataSize; this.PartitionLength = partitionLength; - this.ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator); this.InitBitreader(partitionLength, startPos); } @@ -73,8 +73,8 @@ internal class Vp8BitReader : BitReaderBase /// The partition length. /// Start index in the data array. Defaults to 0. public Vp8BitReader(IMemoryOwner imageData, uint partitionLength, int startPos = 0) + : base(imageData) { - this.Data = imageData; this.ImageDataSize = (uint)imageData.Memory.Length; this.PartitionLength = partitionLength; this.InitBitreader(partitionLength, startPos); diff --git a/src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs b/src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs index 057abf134a..659576cf11 100644 --- a/src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs +++ b/src/ImageSharp/Formats/Webp/BitReader/Vp8LBitReader.cs @@ -63,8 +63,8 @@ internal class Vp8LBitReader : BitReaderBase /// /// Lossless compressed image data. public Vp8LBitReader(IMemoryOwner data) + : base(data) { - this.Data = data; this.len = data.Memory.Length; this.value = 0; this.bitPos = 0; @@ -88,11 +88,10 @@ internal class Vp8LBitReader : BitReaderBase /// The raw image data size in bytes. /// Used for allocating memory during reading data from the stream. public Vp8LBitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator) + : base(inputStream, (int)imageDataSize, memoryAllocator) { long length = imageDataSize; - this.ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator); - this.len = length; this.value = 0; this.bitPos = 0; @@ -193,7 +192,7 @@ internal class Vp8LBitReader : BitReaderBase [MethodImpl(InliningOptions.ShortMethod)] private void ShiftBytes() { - System.Span dataSpan = this.Data.Memory.Span; + Span dataSpan = this.Data!.Memory.Span; while (this.bitPos >= 8 && this.pos < this.len) { this.value >>= 8; diff --git a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs index 2df02727e0..ab78d18604 100644 --- a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs +++ b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Buffers.Binary; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Xmp; @@ -23,7 +24,7 @@ internal abstract class BitWriterBase /// /// A scratch buffer to reduce allocations. /// - private readonly byte[] scratchBuffer = new byte[4]; + private ScratchBuffer scratchBuffer; // mutable struct, don't make readonly /// /// Initializes a new instance of the class. @@ -90,8 +91,8 @@ internal abstract class BitWriterBase protected void WriteRiffHeader(Stream stream, uint riffSize) { stream.Write(WebpConstants.RiffFourCc); - BinaryPrimitives.WriteUInt32LittleEndian(this.scratchBuffer, riffSize); - stream.Write(this.scratchBuffer.AsSpan(0, 4)); + BinaryPrimitives.WriteUInt32LittleEndian(this.scratchBuffer.Span, riffSize); + stream.Write(this.scratchBuffer.Span.Slice(0, 4)); stream.Write(WebpConstants.WebpHeader); } @@ -123,12 +124,12 @@ internal abstract class BitWriterBase /// The stream to write to. /// The metadata profile's bytes. /// The chuck type to write. - protected void WriteMetadataProfile(Stream stream, byte[] metadataBytes, WebpChunkType chunkType) + protected void WriteMetadataProfile(Stream stream, byte[]? metadataBytes, WebpChunkType chunkType) { DebugGuard.NotNull(metadataBytes, nameof(metadataBytes)); uint size = (uint)metadataBytes.Length; - Span buf = this.scratchBuffer.AsSpan(0, 4); + Span buf = this.scratchBuffer.Span.Slice(0, 4); BinaryPrimitives.WriteUInt32BigEndian(buf, (uint)chunkType); stream.Write(buf); BinaryPrimitives.WriteUInt32LittleEndian(buf, size); @@ -151,7 +152,7 @@ internal abstract class BitWriterBase protected void WriteAlphaChunk(Stream stream, Span dataBytes, bool alphaDataIsCompressed) { uint size = (uint)dataBytes.Length + 1; - Span buf = this.scratchBuffer.AsSpan(0, 4); + Span buf = this.scratchBuffer.Span.Slice(0, 4); BinaryPrimitives.WriteUInt32BigEndian(buf, (uint)WebpChunkType.Alpha); stream.Write(buf); BinaryPrimitives.WriteUInt32LittleEndian(buf, size); @@ -182,7 +183,7 @@ internal abstract class BitWriterBase { uint size = (uint)iccProfileBytes.Length; - Span buf = this.scratchBuffer.AsSpan(0, 4); + Span buf = this.scratchBuffer.Span.Slice(0, 4); BinaryPrimitives.WriteUInt32BigEndian(buf, (uint)WebpChunkType.Iccp); stream.Write(buf); BinaryPrimitives.WriteUInt32LittleEndian(buf, size); @@ -207,7 +208,7 @@ internal abstract class BitWriterBase /// The width of the image. /// The height of the image. /// Flag indicating, if a alpha channel is present. - protected void WriteVp8XHeader(Stream stream, ExifProfile exifProfile, XmpProfile xmpProfile, byte[] iccProfileBytes, uint width, uint height, bool hasAlpha) + protected void WriteVp8XHeader(Stream stream, ExifProfile? exifProfile, XmpProfile? xmpProfile, byte[]? iccProfileBytes, uint width, uint height, bool hasAlpha) { if (width > MaxDimension || height > MaxDimension) { @@ -245,7 +246,7 @@ internal abstract class BitWriterBase flags |= 32; } - Span buf = this.scratchBuffer.AsSpan(0, 4); + Span buf = this.scratchBuffer.Span.Slice(0, 4); stream.Write(WebpConstants.Vp8XMagicBytes); BinaryPrimitives.WriteUInt32LittleEndian(buf, WebpConstants.Vp8XChunkSize); stream.Write(buf); @@ -256,4 +257,12 @@ internal abstract class BitWriterBase BinaryPrimitives.WriteUInt32LittleEndian(buf, height - 1); stream.Write(buf[..3]); } + + private unsafe struct ScratchBuffer + { + private const int Size = 4; + private fixed byte scratch[Size]; + + public Span Span => MemoryMarshal.CreateSpan(ref this.scratch[0], Size); + } } diff --git a/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs b/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs index 6fa02c1161..5b4eab64a3 100644 --- a/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs +++ b/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers.Binary; using SixLabors.ImageSharp.Formats.Webp.Lossy; @@ -58,7 +57,8 @@ internal class Vp8BitWriter : BitWriterBase /// Initializes a new instance of the class. /// /// The expected size in bytes. - public Vp8BitWriter(int expectedSize) + /// The Vp8Encoder. + public Vp8BitWriter(int expectedSize, Vp8Encoder enc) : base(expectedSize) { this.range = 255 - 1; @@ -67,15 +67,9 @@ internal class Vp8BitWriter : BitWriterBase this.nbBits = -8; this.pos = 0; this.maxPos = 0; - } - /// - /// Initializes a new instance of the class. - /// - /// The expected size in bytes. - /// The Vp8Encoder. - public Vp8BitWriter(int expectedSize, Vp8Encoder enc) - : this(expectedSize) => this.enc = enc; + this.enc = enc; + } /// public override int NumBytes() => (int)this.pos; @@ -414,9 +408,9 @@ internal class Vp8BitWriter : BitWriterBase /// Indicates, if the alpha data is compressed. public void WriteEncodedImageToStream( Stream stream, - ExifProfile exifProfile, - XmpProfile xmpProfile, - IccProfile iccProfile, + ExifProfile? exifProfile, + XmpProfile? xmpProfile, + IccProfile? iccProfile, uint width, uint height, bool hasAlpha, @@ -424,22 +418,22 @@ internal class Vp8BitWriter : BitWriterBase bool alphaDataIsCompressed) { bool isVp8X = false; - byte[] exifBytes = null; - byte[] xmpBytes = null; - byte[] iccProfileBytes = null; + byte[]? exifBytes = null; + byte[]? xmpBytes = null; + byte[]? iccProfileBytes = null; uint riffSize = 0; if (exifProfile != null) { isVp8X = true; exifBytes = exifProfile.ToByteArray(); - riffSize += MetadataChunkSize(exifBytes); + riffSize += MetadataChunkSize(exifBytes!); } if (xmpProfile != null) { isVp8X = true; xmpBytes = xmpProfile.Data; - riffSize += MetadataChunkSize(xmpBytes); + riffSize += MetadataChunkSize(xmpBytes!); } if (iccProfile != null) @@ -463,9 +457,9 @@ internal class Vp8BitWriter : BitWriterBase this.Finish(); uint numBytes = (uint)this.NumBytes(); int mbSize = this.enc.Mbw * this.enc.Mbh; - int expectedSize = mbSize * 7 / 8; + int expectedSize = (int)((uint)mbSize * 7 / 8); - var bitWriterPartZero = new Vp8BitWriter(expectedSize); + Vp8BitWriter bitWriterPartZero = new(expectedSize, this.enc); // Partition #0 with header and partition sizes. uint size0 = this.GeneratePartition0(bitWriterPartZero); @@ -676,9 +670,9 @@ internal class Vp8BitWriter : BitWriterBase bool isVp8X, uint width, uint height, - ExifProfile exifProfile, - XmpProfile xmpProfile, - byte[] iccProfileBytes, + ExifProfile? exifProfile, + XmpProfile? xmpProfile, + byte[]? iccProfileBytes, bool hasAlpha, Span alphaData, bool alphaDataIsCompressed) diff --git a/src/ImageSharp/Formats/Webp/BitWriter/Vp8LBitWriter.cs b/src/ImageSharp/Formats/Webp/BitWriter/Vp8LBitWriter.cs index 42c1af8040..9dc7912392 100644 --- a/src/ImageSharp/Formats/Webp/BitWriter/Vp8LBitWriter.cs +++ b/src/ImageSharp/Formats/Webp/BitWriter/Vp8LBitWriter.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers.Binary; using SixLabors.ImageSharp.Formats.Webp.Lossless; @@ -15,11 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter; /// internal class Vp8LBitWriter : BitWriterBase { - /// - /// A scratch buffer to reduce allocations. - /// - private readonly byte[] scratchBuffer = new byte[8]; - /// /// This is the minimum amount of size the memory buffer is guaranteed to grow when extra space is needed. /// @@ -138,25 +132,25 @@ internal class Vp8LBitWriter : BitWriterBase /// The width of the image. /// The height of the image. /// Flag indicating, if a alpha channel is present. - public void WriteEncodedImageToStream(Stream stream, ExifProfile exifProfile, XmpProfile xmpProfile, IccProfile iccProfile, uint width, uint height, bool hasAlpha) + public void WriteEncodedImageToStream(Stream stream, ExifProfile? exifProfile, XmpProfile? xmpProfile, IccProfile? iccProfile, uint width, uint height, bool hasAlpha) { bool isVp8X = false; - byte[] exifBytes = null; - byte[] xmpBytes = null; - byte[] iccBytes = null; + byte[]? exifBytes = null; + byte[]? xmpBytes = null; + byte[]? iccBytes = null; uint riffSize = 0; if (exifProfile != null) { isVp8X = true; exifBytes = exifProfile.ToByteArray(); - riffSize += MetadataChunkSize(exifBytes); + riffSize += MetadataChunkSize(exifBytes!); } if (xmpProfile != null) { isVp8X = true; xmpBytes = xmpProfile.Data; - riffSize += MetadataChunkSize(xmpBytes); + riffSize += MetadataChunkSize(xmpBytes!); } if (iccProfile != null) @@ -195,8 +189,9 @@ internal class Vp8LBitWriter : BitWriterBase stream.Write(WebpConstants.Vp8LMagicBytes); // Write Vp8 Header. - BinaryPrimitives.WriteUInt32LittleEndian(this.scratchBuffer, size); - stream.Write(this.scratchBuffer.AsSpan(0, 4)); + Span scratchBuffer = stackalloc byte[8]; + BinaryPrimitives.WriteUInt32LittleEndian(scratchBuffer, size); + stream.Write(scratchBuffer.Slice(0, 4)); stream.WriteByte(WebpConstants.Vp8LHeaderMagicByte); // Write the encoded bytes of the image to the stream. @@ -229,8 +224,9 @@ internal class Vp8LBitWriter : BitWriterBase this.BitWriterResize(extraSize); } - BinaryPrimitives.WriteUInt64LittleEndian(this.scratchBuffer, this.bits); - this.scratchBuffer.AsSpan(0, 4).CopyTo(this.Buffer.AsSpan(this.cur)); + Span scratchBuffer = stackalloc byte[8]; + BinaryPrimitives.WriteUInt64LittleEndian(scratchBuffer, this.bits); + scratchBuffer.Slice(0, 4).CopyTo(this.Buffer.AsSpan(this.cur)); this.cur += WriterBytes; this.bits >>= WriterBits; diff --git a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs index b3589b52c7..61133142bf 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using SixLabors.ImageSharp.Memory; @@ -39,7 +38,7 @@ internal static class BackwardReferenceEncoder int width, int height, ReadOnlySpan bgra, - int quality, + uint quality, int lz77TypesToTry, ref int cacheBits, MemoryAllocator memoryAllocator, @@ -50,7 +49,7 @@ internal static class BackwardReferenceEncoder int lz77TypeBest = 0; double bitCostBest = -1; int cacheBitsInitial = cacheBits; - Vp8LHashChain hashChainBox = null; + Vp8LHashChain? hashChainBox = null; var stats = new Vp8LStreaks(); var bitsEntropy = new Vp8LBitEntropy(); for (int lz77Type = 1; lz77TypesToTry > 0; lz77TypesToTry &= ~lz77Type, lz77Type <<= 1) @@ -101,7 +100,7 @@ internal static class BackwardReferenceEncoder // Improve on simple LZ77 but only for high quality (TraceBackwards is costly). if ((lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard || lz77TypeBest == (int)Vp8LLz77Type.Lz77Box) && quality >= 25) { - Vp8LHashChain hashChainTmp = lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard ? hashChain : hashChainBox; + Vp8LHashChain hashChainTmp = lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard ? hashChain : hashChainBox!; BackwardReferencesTraceBackwards(width, height, memoryAllocator, bgra, cacheBits, hashChainTmp, best, worst); var histo = new Vp8LHistogram(worst, cacheBits); double bitCostTrace = histo.EstimateBits(stats, bitsEntropy); @@ -124,7 +123,7 @@ internal static class BackwardReferenceEncoder /// The local color cache is also disabled for the lower (smaller then 25) quality. /// /// Best cache size. - private static int CalculateBestCacheSize(ReadOnlySpan bgra, int quality, Vp8LBackwardRefs refs, int bestCacheBits) + private static int CalculateBestCacheSize(ReadOnlySpan bgra, uint quality, Vp8LBackwardRefs refs, int bestCacheBits) { int cacheBitsMax = quality <= 25 ? 0 : bestCacheBits; if (cacheBitsMax == 0) @@ -140,8 +139,7 @@ internal static class BackwardReferenceEncoder for (int i = 0; i <= WebpConstants.MaxColorCacheBits; i++) { histos[i] = new Vp8LHistogram(paletteCodeBits: i); - colorCache[i] = new ColorCache(); - colorCache[i].Init(i); + colorCache[i] = new ColorCache(i); } // Find the cacheBits giving the lowest entropy. @@ -274,11 +272,11 @@ internal static class BackwardReferenceEncoder double offsetCost = -1; int firstOffsetIsConstant = -1; // initialized with 'impossible' value. int reach = 0; - var colorCache = new ColorCache(); + ColorCache? colorCache = null; if (useColorCache) { - colorCache.Init(cacheBits); + colorCache = new ColorCache(cacheBits); } costModel.Build(xSize, cacheBits, refs); @@ -375,12 +373,12 @@ internal static class BackwardReferenceEncoder private static void BackwardReferencesHashChainFollowChosenPath(ReadOnlySpan bgra, int cacheBits, Span chosenPath, int chosenPathSize, Vp8LHashChain hashChain, Vp8LBackwardRefs backwardRefs) { bool useColorCache = cacheBits > 0; - var colorCache = new ColorCache(); + ColorCache? colorCache = null; int i = 0; if (useColorCache) { - colorCache.Init(cacheBits); + colorCache = new ColorCache(cacheBits); } backwardRefs.Refs.Clear(); @@ -396,7 +394,7 @@ internal static class BackwardReferenceEncoder { for (int k = 0; k < len; k++) { - colorCache.Insert(bgra[i + k]); + colorCache!.Insert(bgra[i + k]); } } @@ -405,7 +403,7 @@ internal static class BackwardReferenceEncoder else { PixOrCopy v; - int idx = useColorCache ? colorCache.Contains(bgra[i]) : -1; + int idx = useColorCache ? colorCache!.Contains(bgra[i]) : -1; if (idx >= 0) { // useColorCache is true and color cache contains bgra[i] @@ -416,7 +414,7 @@ internal static class BackwardReferenceEncoder { if (useColorCache) { - colorCache.Insert(bgra[i]); + colorCache!.Insert(bgra[i]); } v = PixOrCopy.CreateLiteral(bgra[i]); @@ -430,7 +428,7 @@ internal static class BackwardReferenceEncoder private static void AddSingleLiteralWithCostModel( ReadOnlySpan bgra, - ColorCache colorCache, + ColorCache? colorCache, CostModel costModel, int idx, bool useColorCache, @@ -440,7 +438,7 @@ internal static class BackwardReferenceEncoder { double costVal = prevCost; uint color = bgra[idx]; - int ix = useColorCache ? colorCache.Contains(color) : -1; + int ix = useColorCache ? colorCache!.Contains(color) : -1; if (ix >= 0) { double mul0 = 0.68; @@ -451,7 +449,7 @@ internal static class BackwardReferenceEncoder double mul1 = 0.82; if (useColorCache) { - colorCache.Insert(color); + colorCache!.Insert(color); } costVal += costModel.GetLiteralCost(color) * mul1; @@ -469,10 +467,10 @@ internal static class BackwardReferenceEncoder int iLastCheck = -1; bool useColorCache = cacheBits > 0; int pixCount = xSize * ySize; - var colorCache = new ColorCache(); + ColorCache? colorCache = null; if (useColorCache) { - colorCache.Init(cacheBits); + colorCache = new ColorCache(cacheBits); } refs.Refs.Clear(); @@ -529,7 +527,7 @@ internal static class BackwardReferenceEncoder { for (j = i; j < i + len; j++) { - colorCache.Insert(bgra[j]); + colorCache!.Insert(bgra[j]); } } } @@ -725,11 +723,11 @@ internal static class BackwardReferenceEncoder { int pixelCount = xSize * ySize; bool useColorCache = cacheBits > 0; - var colorCache = new ColorCache(); + ColorCache? colorCache = null; if (useColorCache) { - colorCache.Init(cacheBits); + colorCache = new ColorCache(cacheBits); } refs.Refs.Clear(); @@ -757,7 +755,7 @@ internal static class BackwardReferenceEncoder { for (int k = 0; k < prevRowLen; ++k) { - colorCache.Insert(bgra[i + k]); + colorCache!.Insert(bgra[i + k]); } } @@ -777,8 +775,7 @@ internal static class BackwardReferenceEncoder private static void BackwardRefsWithLocalCache(ReadOnlySpan bgra, int cacheBits, Vp8LBackwardRefs refs) { int pixelIndex = 0; - var colorCache = new ColorCache(); - colorCache.Init(cacheBits); + ColorCache colorCache = new(cacheBits); for (int idx = 0; idx < refs.Refs.Count; idx++) { PixOrCopy v = refs.Refs[idx]; @@ -825,12 +822,12 @@ internal static class BackwardReferenceEncoder } } - private static void AddSingleLiteral(uint pixel, bool useColorCache, ColorCache colorCache, Vp8LBackwardRefs refs) + private static void AddSingleLiteral(uint pixel, bool useColorCache, ColorCache? colorCache, Vp8LBackwardRefs refs) { PixOrCopy v; if (useColorCache) { - int key = colorCache.GetIndex(pixel); + int key = colorCache!.GetIndex(pixel); if (colorCache.Lookup(key) == pixel) { v = PixOrCopy.CreateCacheIdx(key); diff --git a/src/ImageSharp/Formats/Webp/Lossless/ColorCache.cs b/src/ImageSharp/Formats/Webp/Lossless/ColorCache.cs index 08ff60b69f..e683fb5605 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/ColorCache.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/ColorCache.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Runtime.CompilerServices; @@ -14,31 +13,31 @@ internal class ColorCache private const uint HashMul = 0x1e35a7bdu; /// - /// Gets the color entries. + /// Initializes a new instance of the class. /// - public uint[] Colors { get; private set; } + /// The hashBits determine the size of cache. It will be 1 left shifted by hashBits. + public ColorCache(int hashBits) + { + int hashSize = 1 << hashBits; + this.Colors = new uint[hashSize]; + this.HashBits = hashBits; + this.HashShift = 32 - hashBits; + } /// - /// Gets the hash shift: 32 - hashBits. + /// Gets the color entries. /// - public int HashShift { get; private set; } + public uint[] Colors { get; } /// - /// Gets the hash bits. + /// Gets the hash shift: 32 - hashBits. /// - public int HashBits { get; private set; } + public int HashShift { get; } /// - /// Initializes a new color cache. + /// Gets the hash bits. /// - /// The hashBits determine the size of cache. It will be 1 left shifted by hashBits. - public void Init(int hashBits) - { - int hashSize = 1 << hashBits; - this.Colors = new uint[hashSize]; - this.HashBits = hashBits; - this.HashShift = 32 - hashBits; - } + public int HashBits { get; } /// /// Inserts a new color into the cache. diff --git a/src/ImageSharp/Formats/Webp/Lossless/ColorSpaceTransformUtils.cs b/src/ImageSharp/Formats/Webp/Lossless/ColorSpaceTransformUtils.cs index 45de1b9553..9a6dfb66e8 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/ColorSpaceTransformUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/ColorSpaceTransformUtils.cs @@ -27,10 +27,10 @@ internal static class ColorSpaceTransformUtils { Span srcSpan = bgra[(y * stride)..]; ref uint inputRef = ref MemoryMarshal.GetReference(srcSpan); - for (nint x = 0; x <= tileWidth - span; x += span) + for (nuint x = 0; x <= (uint)tileWidth - span; x += span) { - nint input0Idx = x; - nint input1Idx = x + (span / 2); + nuint input0Idx = x; + nuint input1Idx = x + (span / 2); Vector256 input0 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input0Idx)).AsByte(); Vector256 input1 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input1Idx)).AsByte(); Vector256 r0 = Avx2.Shuffle(input0, collectColorBlueTransformsShuffleLowMask256); @@ -77,10 +77,10 @@ internal static class ColorSpaceTransformUtils { Span srcSpan = bgra[(y * stride)..]; ref uint inputRef = ref MemoryMarshal.GetReference(srcSpan); - for (nint x = 0; x <= tileWidth - span; x += span) + for (nuint x = 0; (int)x <= tileWidth - span; x += span) { - nint input0Idx = x; - nint input1Idx = x + (span / 2); + nuint input0Idx = x; + nuint input1Idx = x + (span / 2); Vector128 input0 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input0Idx)).AsByte(); Vector128 input1 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input1Idx)).AsByte(); Vector128 r0 = Ssse3.Shuffle(input0, collectColorBlueTransformsShuffleLowMask); @@ -146,10 +146,10 @@ internal static class ColorSpaceTransformUtils { Span srcSpan = bgra[(y * stride)..]; ref uint inputRef = ref MemoryMarshal.GetReference(srcSpan); - for (nint x = 0; x <= tileWidth - span; x += span) + for (nuint x = 0; x <= (uint)tileWidth - span; x += span) { - nint input0Idx = x; - nint input1Idx = x + (span / 2); + nuint input0Idx = x; + nuint input1Idx = x + (span / 2); Vector256 input0 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input0Idx)).AsByte(); Vector256 input1 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input1Idx)).AsByte(); Vector256 g0 = Avx2.And(input0, collectColorRedTransformsGreenMask256); // 0 0 | g 0 @@ -189,10 +189,10 @@ internal static class ColorSpaceTransformUtils { Span srcSpan = bgra[(y * stride)..]; ref uint inputRef = ref MemoryMarshal.GetReference(srcSpan); - for (nint x = 0; x <= tileWidth - span; x += span) + for (nuint x = 0; (int)x <= tileWidth - span; x += span) { - nint input0Idx = x; - nint input1Idx = x + (span / 2); + nuint input0Idx = x; + nuint input1Idx = x + (span / 2); Vector128 input0 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input0Idx)).AsByte(); Vector128 input1 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input1Idx)).AsByte(); Vector128 g0 = Sse2.And(input0, collectColorRedTransformsGreenMask); // 0 0 | g 0 diff --git a/src/ImageSharp/Formats/Webp/Lossless/CostInterval.cs b/src/ImageSharp/Formats/Webp/Lossless/CostInterval.cs index a63c786049..0cc4a30fd7 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/CostInterval.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/CostInterval.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Diagnostics; @@ -33,7 +32,7 @@ internal class CostInterval public int Index { get; set; } - public CostInterval Previous { get; set; } + public CostInterval? Previous { get; set; } - public CostInterval Next { get; set; } + public CostInterval? Next { get; set; } } diff --git a/src/ImageSharp/Formats/Webp/Lossless/CostManager.cs b/src/ImageSharp/Formats/Webp/Lossless/CostManager.cs index 7b9fdff247..e393c065ec 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/CostManager.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/CostManager.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using SixLabors.ImageSharp.Memory; @@ -14,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless; /// internal sealed class CostManager : IDisposable { - private CostInterval head; + private CostInterval? head; private const int FreeIntervalsStartCount = 25; @@ -103,10 +102,10 @@ internal sealed class CostManager : IDisposable /// If 'doCleanIntervals' is true, intervals that end before 'i' will be popped. public void UpdateCostAtIndex(int i, bool doCleanIntervals) { - CostInterval current = this.head; + CostInterval? current = this.head; while (current != null && current.Start <= i) { - CostInterval next = current.Next; + CostInterval? next = current.Next; if (current.End <= i) { if (doCleanIntervals) @@ -155,7 +154,7 @@ internal sealed class CostManager : IDisposable return; } - CostInterval interval = this.head; + CostInterval? interval = this.head; for (int i = 0; i < this.CacheIntervalsSize && this.CacheIntervals[i].Start < len; i++) { // Define the intersection of the ith interval with the new one. @@ -163,7 +162,7 @@ internal sealed class CostManager : IDisposable int end = position + (this.CacheIntervals[i].End > len ? len : this.CacheIntervals[i].End); float cost = (float)(distanceCost + this.CacheIntervals[i].Cost); - CostInterval intervalNext; + CostInterval? intervalNext; for (; interval != null && interval.Start < end; interval = intervalNext) { intervalNext = interval.Next; @@ -225,7 +224,7 @@ internal sealed class CostManager : IDisposable /// Pop an interval from the manager. /// /// The interval to remove. - private void PopInterval(CostInterval interval) + private void PopInterval(CostInterval? interval) { if (interval == null) { @@ -240,7 +239,7 @@ internal sealed class CostManager : IDisposable this.freeIntervals.Push(interval); } - private void InsertInterval(CostInterval intervalIn, float cost, int position, int start, int end) + private void InsertInterval(CostInterval? intervalIn, float cost, int position, int start, int end) { if (start >= end) { @@ -271,7 +270,7 @@ internal sealed class CostManager : IDisposable /// it was orphaned (which can be NULL), set it at the right place in the list /// of intervals using the start_ ordering and the previous interval as a hint. /// - private void PositionOrphanInterval(CostInterval current, CostInterval previous) + private void PositionOrphanInterval(CostInterval current, CostInterval? previous) { previous ??= this.head; @@ -292,7 +291,7 @@ internal sealed class CostManager : IDisposable /// /// Given two intervals, make 'prev' be the previous one of 'next' in 'manager'. /// - private void ConnectIntervals(CostInterval prev, CostInterval next) + private void ConnectIntervals(CostInterval? prev, CostInterval? next) { if (prev != null) { diff --git a/src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs index 1395181634..dd59ed2097 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs @@ -6,7 +6,7 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Formats.Webp.Lossless; -internal class HistogramEncoder +internal static class HistogramEncoder { /// /// Number of partitions for the three dominant (literal, red and blue) symbol costs. @@ -27,7 +27,7 @@ internal class HistogramEncoder private const ushort InvalidHistogramSymbol = ushort.MaxValue; - public static void GetHistoImageSymbols(int xSize, int ySize, Vp8LBackwardRefs refs, int quality, int histoBits, int cacheBits, List imageHisto, Vp8LHistogram tmpHisto, ushort[] histogramSymbols) + public static void GetHistoImageSymbols(int xSize, int ySize, Vp8LBackwardRefs refs, uint quality, int histoBits, int cacheBits, List imageHisto, Vp8LHistogram tmpHisto, Span histogramSymbols) { int histoXSize = histoBits > 0 ? LosslessUtils.SubSampleSize(xSize, histoBits) : 1; int histoYSize = histoBits > 0 ? LosslessUtils.SubSampleSize(ySize, histoBits) : 1; @@ -148,7 +148,7 @@ internal class HistogramEncoder } } - private static int HistogramCopyAndAnalyze(List origHistograms, List histograms, ushort[] histogramSymbols) + private static int HistogramCopyAndAnalyze(List origHistograms, List histograms, Span histogramSymbols) { var stats = new Vp8LStreaks(); var bitsEntropy = new Vp8LBitEntropy(); @@ -171,20 +171,28 @@ internal class HistogramEncoder } } - int numUsed = histogramSymbols.Count(h => h != InvalidHistogramSymbol); + int numUsed = 0; + foreach (ushort h in histogramSymbols) + { + if (h != InvalidHistogramSymbol) + { + numUsed++; + } + } + return numUsed; } private static void HistogramCombineEntropyBin( List histograms, - ushort[] clusters, + Span clusters, ushort[] clusterMappings, Vp8LHistogram curCombo, ushort[] binMap, int numBins, double combineCostFactor) { - var binInfo = new HistogramBinInfo[BinSize]; + Span binInfo = stackalloc HistogramBinInfo[BinSize]; for (int idx = 0; idx < numBins; idx++) { binInfo[idx].First = -1; @@ -258,7 +266,7 @@ internal class HistogramEncoder /// Given a Histogram set, the mapping of clusters 'clusterMapping' and the /// current assignment of the cells in 'symbols', merge the clusters and assign the smallest possible clusters values. /// - private static void OptimizeHistogramSymbols(ushort[] clusterMappings, int numClusters, ushort[] clusterMappingsTmp, ushort[] symbols) + private static void OptimizeHistogramSymbols(ushort[] clusterMappings, int numClusters, ushort[] clusterMappingsTmp, Span symbols) { bool doContinue = true; @@ -316,7 +324,7 @@ internal class HistogramEncoder int triesWithNoSuccess = 0; int numUsed = histograms.Count(h => h != null); int outerIters = numUsed; - int numTriesNoSuccess = outerIters / 2; + int numTriesNoSuccess = (int)((uint)outerIters / 2); var stats = new Vp8LStreaks(); var bitsEntropy = new Vp8LBitEntropy(); @@ -331,7 +339,7 @@ internal class HistogramEncoder int maxSize = 9; // Fill the initial mapping. - int[] mappings = new int[histograms.Count]; + Span mappings = histograms.Count <= 64 ? stackalloc int[histograms.Count] : new int[histograms.Count]; for (int j = 0, iter = 0; iter < histograms.Count; iter++) { if (histograms[iter] == null) @@ -346,7 +354,7 @@ internal class HistogramEncoder for (int iter = 0; iter < outerIters && numUsed >= minClusterSize && ++triesWithNoSuccess < numTriesNoSuccess; iter++) { double bestCost = histoPriorityList.Count == 0 ? 0.0d : histoPriorityList[0].CostDiff; - int numTries = numUsed / 2; + int numTries = (int)((uint)numUsed / 2); uint randRange = (uint)((numUsed - 1) * numUsed); // Pick random samples. @@ -388,9 +396,9 @@ internal class HistogramEncoder int bestIdx1 = histoPriorityList[0].Idx1; int bestIdx2 = histoPriorityList[0].Idx2; - int mappingIndex = Array.IndexOf(mappings, bestIdx2); - Span src = mappings.AsSpan(mappingIndex + 1, numUsed - mappingIndex - 1); - Span dst = mappings.AsSpan(mappingIndex); + int mappingIndex = mappings.IndexOf(bestIdx2); + Span src = mappings.Slice(mappingIndex + 1, numUsed - mappingIndex - 1); + Span dst = mappings.Slice(mappingIndex); src.CopyTo(dst); // Merge the histograms and remove bestIdx2 from the list. @@ -528,7 +536,7 @@ internal class HistogramEncoder } } - private static void HistogramRemap(List input, List output, ushort[] symbols) + private static void HistogramRemap(List input, List output, Span symbols) { int inSize = input.Count; int outSize = output.Count; @@ -660,7 +668,7 @@ internal class HistogramEncoder output.TrivialSymbol = a.TrivialSymbol == b.TrivialSymbol ? a.TrivialSymbol : NonTrivialSym; } - private static double GetCombineCostFactor(int histoSize, int quality) + private static double GetCombineCostFactor(int histoSize, uint quality) { double combineCostFactor = 0.16d; if (quality < 90) diff --git a/src/ImageSharp/Formats/Webp/Lossless/HuffmanUtils.cs b/src/ImageSharp/Formats/Webp/Lossless/HuffmanUtils.cs index 5cee6bc396..39ad967e38 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/HuffmanUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/HuffmanUtils.cs @@ -25,7 +25,7 @@ internal static class HuffmanUtils 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf }; - public static void CreateHuffmanTree(uint[] histogram, int treeDepthLimit, bool[] bufRle, HuffmanTree[] huffTree, HuffmanTreeCode huffCode) + public static void CreateHuffmanTree(uint[] histogram, int treeDepthLimit, bool[] bufRle, Span huffTree, HuffmanTreeCode huffCode) { int numSymbols = huffCode.NumSymbols; bufRle.AsSpan().Clear(); @@ -100,7 +100,7 @@ internal static class HuffmanUtils uint k; // The stride must end, collapse what we have, if we have enough (4). - uint count = (uint)((sum + (stride / 2)) / stride); + uint count = (sum + ((uint)stride / 2)) / (uint)stride; if (count < 1) { count = 1; @@ -144,7 +144,7 @@ internal static class HuffmanUtils sum += counts[i]; if (stride >= 4) { - limit = (uint)((sum + (stride / 2)) / stride); + limit = (sum + ((uint)stride / 2)) / (uint)stride; } } } @@ -159,7 +159,7 @@ internal static class HuffmanUtils /// The size of the histogram. /// The tree depth limit. /// How many bits are used for the symbol. - public static void GenerateOptimalTree(HuffmanTree[] tree, uint[] histogram, int histogramSize, int treeDepthLimit, byte[] bitDepths) + public static void GenerateOptimalTree(Span tree, uint[] histogram, int histogramSize, int treeDepthLimit, byte[] bitDepths) { uint countMin; int treeSizeOrig = 0; @@ -177,7 +177,7 @@ internal static class HuffmanUtils return; } - Span treePool = tree.AsSpan(treeSizeOrig); + Span treePool = tree.Slice(treeSizeOrig); // For block sizes with less than 64k symbols we never need to do a // second iteration of this loop. @@ -202,14 +202,8 @@ internal static class HuffmanUtils } // Build the Huffman tree. -#if NET5_0_OR_GREATER - Span treeSlice = tree.AsSpan(0, treeSize); + Span treeSlice = tree.Slice(0, treeSize); treeSlice.Sort(HuffmanTree.Compare); -#else - HuffmanTree[] treeCopy = tree.AsSpan(0, treeSize).ToArray(); - Array.Sort(treeCopy, HuffmanTree.Compare); - treeCopy.AsSpan().CopyTo(tree); -#endif if (treeSize > 1) { @@ -312,12 +306,12 @@ internal static class HuffmanUtils DebugGuard.MustBeGreaterThan(codeLengthsSize, 0, nameof(codeLengthsSize)); // sorted[codeLengthsSize] is a pre-allocated array for sorting symbols by code length. - int[] sorted = new int[codeLengthsSize]; + Span sorted = codeLengthsSize <= 64 ? stackalloc int[codeLengthsSize] : new int[codeLengthsSize]; int totalSize = 1 << rootBits; // total size root table + 2nd level table. int len; // current code length. int symbol; // symbol index in original or sorted table. - int[] counts = new int[WebpConstants.MaxAllowedCodeLength + 1]; // number of codes of each length. - int[] offsets = new int[WebpConstants.MaxAllowedCodeLength + 1]; // offsets in sorted table for each length. + Span counts = stackalloc int[WebpConstants.MaxAllowedCodeLength + 1]; // number of codes of each length. + Span offsets = stackalloc int[WebpConstants.MaxAllowedCodeLength + 1]; // offsets in sorted table for each length. // Build histogram of code lengths. for (symbol = 0; symbol < codeLengthsSize; ++symbol) @@ -544,8 +538,8 @@ internal static class HuffmanUtils private static void ConvertBitDepthsToSymbols(HuffmanTreeCode tree) { // 0 bit-depth means that the symbol does not exist. - uint[] nextCode = new uint[WebpConstants.MaxAllowedCodeLength + 1]; - int[] depthCount = new int[WebpConstants.MaxAllowedCodeLength + 1]; + Span nextCode = stackalloc uint[WebpConstants.MaxAllowedCodeLength + 1]; + Span depthCount = stackalloc int[WebpConstants.MaxAllowedCodeLength + 1]; int len = tree.NumSymbols; for (int i = 0; i < len; i++) @@ -603,7 +597,7 @@ internal static class HuffmanUtils /// Returns the table width of the next 2nd level table. count is the histogram of bit lengths for the remaining symbols, /// len is the code length of the next processed symbol. /// - private static int NextTableBitSize(int[] count, int len, int rootBits) + private static int NextTableBitSize(ReadOnlySpan count, int len, int rootBits) { int left = 1 << (len - rootBits); while (len < WebpConstants.MaxAllowedCodeLength) diff --git a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs index 5d9c207096..024adb7c23 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs @@ -51,7 +51,7 @@ internal static unsafe class LosslessUtils ref uint array1Ref = ref MemoryMarshal.GetReference(array1); ref uint array2Ref = ref MemoryMarshal.GetReference(array2); - while (matchLen < length && Unsafe.Add(ref array1Ref, matchLen) == Unsafe.Add(ref array2Ref, matchLen)) + while (matchLen < length && Unsafe.Add(ref array1Ref, (uint)matchLen) == Unsafe.Add(ref array2Ref, (uint)matchLen)) { matchLen++; } @@ -94,60 +94,64 @@ internal static unsafe class LosslessUtils /// The pixel data to apply the transformation. public static void AddGreenToBlueAndRed(Span pixelData) { - if (Avx2.IsSupported) + if (Avx2.IsSupported && pixelData.Length >= 8) { - var addGreenToBlueAndRedMaskAvx2 = Vector256.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255, 17, 255, 17, 255, 21, 255, 21, 255, 25, 255, 25, 255, 29, 255, 29, 255); - int numPixels = pixelData.Length; - nint i; - for (i = 0; i <= numPixels - 8; i += 8) + Vector256 addGreenToBlueAndRedMaskAvx2 = Vector256.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255, 17, 255, 17, 255, 21, 255, 21, 255, 25, 255, 25, 255, 29, 255, 29, 255); + nuint numPixels = (uint)pixelData.Length; + nuint i = 0; + do { ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector256 input = Unsafe.As>(ref pos).AsByte(); Vector256 in0g0g = Avx2.Shuffle(input, addGreenToBlueAndRedMaskAvx2); Vector256 output = Avx2.Add(input, in0g0g); Unsafe.As>(ref pos) = output.AsUInt32(); + i += 8; } + while (i <= numPixels - 8); if (i != numPixels) { AddGreenToBlueAndRedScalar(pixelData[(int)i..]); } } - else if (Ssse3.IsSupported) + else if (Ssse3.IsSupported && pixelData.Length >= 4) { - var addGreenToBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255); - int numPixels = pixelData.Length; - nint i; - for (i = 0; i <= numPixels - 4; i += 4) + Vector128 addGreenToBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255); + nuint numPixels = (uint)pixelData.Length; + nuint i = 0; + do { ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector128 input = Unsafe.As>(ref pos).AsByte(); Vector128 in0g0g = Ssse3.Shuffle(input, addGreenToBlueAndRedMaskSsse3); Vector128 output = Sse2.Add(input, in0g0g); Unsafe.As>(ref pos) = output.AsUInt32(); + i += 4; } + while (i <= numPixels - 4); if (i != numPixels) { AddGreenToBlueAndRedScalar(pixelData[(int)i..]); } } - else if (Sse2.IsSupported) + else if (Sse2.IsSupported && pixelData.Length >= 4) { - int numPixels = pixelData.Length; - nint i; - for (i = 0; i <= numPixels - 4; i += 4) + nuint numPixels = (uint)pixelData.Length; + nuint i = 0; + do { - const byte mmShuffle_2200 = 0b_10_10_00_00; - ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector128 input = Unsafe.As>(ref pos).AsByte(); Vector128 a = Sse2.ShiftRightLogical(input.AsUInt16(), 8); // 0 a 0 g - Vector128 b = Sse2.ShuffleLow(a, mmShuffle_2200); - Vector128 c = Sse2.ShuffleHigh(b, mmShuffle_2200); // 0g0g + Vector128 b = Sse2.ShuffleLow(a, SimdUtils.Shuffle.MMShuffle2200); + Vector128 c = Sse2.ShuffleHigh(b, SimdUtils.Shuffle.MMShuffle2200); // 0g0g Vector128 output = Sse2.Add(input.AsByte(), c.AsByte()); Unsafe.As>(ref pos) = output.AsUInt32(); + i += 4; } + while (i <= numPixels - 4); if (i != numPixels) { @@ -176,60 +180,64 @@ internal static unsafe class LosslessUtils public static void SubtractGreenFromBlueAndRed(Span pixelData) { - if (Avx2.IsSupported) + if (Avx2.IsSupported && pixelData.Length >= 8) { - var subtractGreenFromBlueAndRedMaskAvx2 = Vector256.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255, 17, 255, 17, 255, 21, 255, 21, 255, 25, 255, 25, 255, 29, 255, 29, 255); - int numPixels = pixelData.Length; - nint i; - for (i = 0; i <= numPixels - 8; i += 8) + Vector256 subtractGreenFromBlueAndRedMaskAvx2 = Vector256.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255, 17, 255, 17, 255, 21, 255, 21, 255, 25, 255, 25, 255, 29, 255, 29, 255); + nuint numPixels = (uint)pixelData.Length; + nuint i = 0; + do { ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector256 input = Unsafe.As>(ref pos).AsByte(); Vector256 in0g0g = Avx2.Shuffle(input, subtractGreenFromBlueAndRedMaskAvx2); Vector256 output = Avx2.Subtract(input, in0g0g); Unsafe.As>(ref pos) = output.AsUInt32(); + i += 8; } + while (i <= numPixels - 8); if (i != numPixels) { SubtractGreenFromBlueAndRedScalar(pixelData[(int)i..]); } } - else if (Ssse3.IsSupported) + else if (Ssse3.IsSupported && pixelData.Length >= 4) { - var subtractGreenFromBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255); - int numPixels = pixelData.Length; - nint i; - for (i = 0; i <= numPixels - 4; i += 4) + Vector128 subtractGreenFromBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255); + nuint numPixels = (uint)pixelData.Length; + nuint i = 0; + do { ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector128 input = Unsafe.As>(ref pos).AsByte(); Vector128 in0g0g = Ssse3.Shuffle(input, subtractGreenFromBlueAndRedMaskSsse3); Vector128 output = Sse2.Subtract(input, in0g0g); Unsafe.As>(ref pos) = output.AsUInt32(); + i += 4; } + while (i <= numPixels - 4); if (i != numPixels) { SubtractGreenFromBlueAndRedScalar(pixelData[(int)i..]); } } - else if (Sse2.IsSupported) + else if (Sse2.IsSupported && pixelData.Length >= 4) { - int numPixels = pixelData.Length; - nint i; - for (i = 0; i <= numPixels - 4; i += 4) + nuint numPixels = (uint)pixelData.Length; + nuint i = 0; + do { - const byte mmShuffle_2200 = 0b_10_10_00_00; - ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector128 input = Unsafe.As>(ref pos).AsByte(); Vector128 a = Sse2.ShiftRightLogical(input.AsUInt16(), 8); // 0 a 0 g - Vector128 b = Sse2.ShuffleLow(a, mmShuffle_2200); - Vector128 c = Sse2.ShuffleHigh(b, mmShuffle_2200); // 0g0g + Vector128 b = Sse2.ShuffleLow(a, SimdUtils.Shuffle.MMShuffle2200); + Vector128 c = Sse2.ShuffleHigh(b, SimdUtils.Shuffle.MMShuffle2200); // 0g0g Vector128 output = Sse2.Subtract(input.AsByte(), c.AsByte()); Unsafe.As>(ref pos) = output.AsUInt32(); + i += 4; } + while (i <= numPixels - 4); if (i != numPixels) { @@ -334,7 +342,7 @@ internal static unsafe class LosslessUtils while (y < yEnd) { int predRowIdx = predRowIdxStart; - var m = default(Vp8LMultipliers); + Vp8LMultipliers m = default; int srcSafeEnd = pixelPos + safeWidth; int srcEnd = pixelPos + width; while (pixelPos < srcSafeEnd) @@ -371,21 +379,19 @@ internal static unsafe class LosslessUtils { if (Avx2.IsSupported && numPixels >= 8) { - var transformColorAlphaGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); - var transformColorRedBlueMask256 = Vector256.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); + Vector256 transformColorAlphaGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); + Vector256 transformColorRedBlueMask256 = Vector256.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); Vector256 multsrb = MkCst32(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector256 multsb2 = MkCst32(Cst5b(m.RedToBlue), 0); - nint idx; - for (idx = 0; idx <= numPixels - 8; idx += 8) + nuint idx = 0; + do { - const byte mmShuffle_2200 = 0b_10_10_00_00; - ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), idx); Vector256 input = Unsafe.As>(ref pos); Vector256 a = Avx2.And(input.AsByte(), transformColorAlphaGreenMask256); - Vector256 b = Avx2.ShuffleLow(a.AsInt16(), mmShuffle_2200); - Vector256 c = Avx2.ShuffleHigh(b.AsInt16(), mmShuffle_2200); + Vector256 b = Avx2.ShuffleLow(a.AsInt16(), SimdUtils.Shuffle.MMShuffle2200); + Vector256 c = Avx2.ShuffleHigh(b.AsInt16(), SimdUtils.Shuffle.MMShuffle2200); Vector256 d = Avx2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); Vector256 e = Avx2.ShiftLeftLogical(input.AsInt16(), 8); Vector256 f = Avx2.MultiplyHigh(e.AsInt16(), multsb2.AsInt16()); @@ -394,29 +400,29 @@ internal static unsafe class LosslessUtils Vector256 i = Avx2.And(h, transformColorRedBlueMask256); Vector256 output = Avx2.Subtract(input.AsByte(), i); Unsafe.As>(ref pos) = output.AsUInt32(); + idx += 8; } + while (idx <= (uint)numPixels - 8); - if (idx != numPixels) + if (idx != (uint)numPixels) { TransformColorScalar(m, pixelData[(int)idx..], numPixels - (int)idx); } } - else if (Sse2.IsSupported) + else if (Sse2.IsSupported && numPixels >= 4) { - var transformColorAlphaGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); - var transformColorRedBlueMask = Vector128.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); + Vector128 transformColorAlphaGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); + Vector128 transformColorRedBlueMask = Vector128.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); Vector128 multsrb = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector128 multsb2 = MkCst16(Cst5b(m.RedToBlue), 0); - nint idx; - for (idx = 0; idx <= numPixels - 4; idx += 4) + nuint idx = 0; + do { - const byte mmShuffle_2200 = 0b_10_10_00_00; - ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), idx); Vector128 input = Unsafe.As>(ref pos); Vector128 a = Sse2.And(input.AsByte(), transformColorAlphaGreenMask); - Vector128 b = Sse2.ShuffleLow(a.AsInt16(), mmShuffle_2200); - Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), mmShuffle_2200); + Vector128 b = Sse2.ShuffleLow(a.AsInt16(), SimdUtils.Shuffle.MMShuffle2200); + Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), SimdUtils.Shuffle.MMShuffle2200); Vector128 d = Sse2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); Vector128 e = Sse2.ShiftLeftLogical(input.AsInt16(), 8); Vector128 f = Sse2.MultiplyHigh(e.AsInt16(), multsb2.AsInt16()); @@ -425,9 +431,11 @@ internal static unsafe class LosslessUtils Vector128 i = Sse2.And(h, transformColorRedBlueMask); Vector128 output = Sse2.Subtract(input.AsByte(), i); Unsafe.As>(ref pos) = output.AsUInt32(); + idx += 4; } + while ((int)idx <= numPixels - 4); - if (idx != numPixels) + if ((int)idx != numPixels) { TransformColorScalar(m, pixelData[(int)idx..], numPixels - (int)idx); } @@ -465,19 +473,17 @@ internal static unsafe class LosslessUtils { if (Avx2.IsSupported && pixelData.Length >= 8) { - var transformColorInverseAlphaGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); + Vector256 transformColorInverseAlphaGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); Vector256 multsrb = MkCst32(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector256 multsb2 = MkCst32(Cst5b(m.RedToBlue), 0); - nint idx; - for (idx = 0; idx <= pixelData.Length - 8; idx += 8) + nuint idx; + for (idx = 0; idx <= (uint)pixelData.Length - 8; idx += 8) { - const byte mmShuffle_2200 = 0b_10_10_00_00; - ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), idx); Vector256 input = Unsafe.As>(ref pos); Vector256 a = Avx2.And(input.AsByte(), transformColorInverseAlphaGreenMask256); - Vector256 b = Avx2.ShuffleLow(a.AsInt16(), mmShuffle_2200); - Vector256 c = Avx2.ShuffleHigh(b.AsInt16(), mmShuffle_2200); + Vector256 b = Avx2.ShuffleLow(a.AsInt16(), SimdUtils.Shuffle.MMShuffle2200); + Vector256 c = Avx2.ShuffleHigh(b.AsInt16(), SimdUtils.Shuffle.MMShuffle2200); Vector256 d = Avx2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); Vector256 e = Avx2.Add(input.AsByte(), d.AsByte()); Vector256 f = Avx2.ShiftLeftLogical(e.AsInt16(), 8); @@ -489,27 +495,25 @@ internal static unsafe class LosslessUtils Unsafe.As>(ref pos) = output.AsUInt32(); } - if (idx != pixelData.Length) + if (idx != (uint)pixelData.Length) { TransformColorInverseScalar(m, pixelData[(int)idx..]); } } - else if (Sse2.IsSupported) + else if (Sse2.IsSupported && pixelData.Length >= 4) { - var transformColorInverseAlphaGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); + Vector128 transformColorInverseAlphaGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); Vector128 multsrb = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector128 multsb2 = MkCst16(Cst5b(m.RedToBlue), 0); - nint idx; - for (idx = 0; idx <= pixelData.Length - 4; idx += 4) + nuint idx; + for (idx = 0; idx <= (uint)pixelData.Length - 4; idx += 4) { - const byte mmShuffle_2200 = 0b_10_10_00_00; - ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), idx); Vector128 input = Unsafe.As>(ref pos); Vector128 a = Sse2.And(input.AsByte(), transformColorInverseAlphaGreenMask); - Vector128 b = Sse2.ShuffleLow(a.AsInt16(), mmShuffle_2200); - Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), mmShuffle_2200); + Vector128 b = Sse2.ShuffleLow(a.AsInt16(), SimdUtils.Shuffle.MMShuffle2200); + Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), SimdUtils.Shuffle.MMShuffle2200); Vector128 d = Sse2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); Vector128 e = Sse2.Add(input.AsByte(), d.AsByte()); Vector128 f = Sse2.ShiftLeftLogical(e.AsInt16(), 8); @@ -521,7 +525,7 @@ internal static unsafe class LosslessUtils Unsafe.As>(ref pos) = output.AsUInt32(); } - if (idx != pixelData.Length) + if (idx != (uint)pixelData.Length) { TransformColorInverseScalar(m, pixelData[(int)idx..]); } @@ -752,7 +756,7 @@ internal static unsafe class LosslessUtils Vector256 sumXY256 = Vector256.Zero; Vector256 sumX256 = Vector256.Zero; ref int tmpRef = ref Unsafe.As, int>(ref tmp); - for (nint i = 0; i < 256; i += 8) + for (nuint i = 0; i < 256; i += 8) { Vector256 xVec = Unsafe.As>(ref Unsafe.Add(ref xRef, i)); Vector256 yVec = Unsafe.As>(ref Unsafe.Add(ref yRef, i)); @@ -1470,8 +1474,7 @@ internal static unsafe class LosslessUtils { if (Sse2.IsSupported) { - Span output = scratch; - fixed (short* p = output) + fixed (short* ptr = &MemoryMarshal.GetReference(scratch)) { Vector128 a0 = Sse2.ConvertScalarToVector128UInt32(a).AsByte(); Vector128 b0 = Sse2.ConvertScalarToVector128UInt32(b).AsByte(); @@ -1485,8 +1488,8 @@ internal static unsafe class LosslessUtils Vector128 pa = Sse2.UnpackLow(ac, Vector128.Zero); // |a - c| Vector128 pb = Sse2.UnpackLow(bc, Vector128.Zero); // |b - c| Vector128 diff = Sse2.Subtract(pb.AsUInt16(), pa.AsUInt16()); - Sse2.Store((ushort*)p, diff); - int paMinusPb = output[3] + output[2] + output[1] + output[0]; + Sse2.Store((ushort*)ptr, diff); + int paMinusPb = ptr[3] + ptr[2] + ptr[1] + ptr[0]; return (paMinusPb <= 0) ? a : b; } } diff --git a/src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs index 5d8d3203b1..2170eb1985 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs @@ -57,11 +57,13 @@ internal static unsafe class PredictorEncoder Span scratch = stackalloc short[8]; // TODO: Can we optimize this? - int[][] histo = new int[4][]; - for (int i = 0; i < 4; i++) + int[][] histo = { - histo[i] = new int[256]; - } + new int[256], + new int[256], + new int[256], + new int[256] + }; if (lowEffort) { @@ -113,7 +115,7 @@ internal static unsafe class PredictorEncoder lowEffort); } - public static void ColorSpaceTransform(int width, int height, int bits, int quality, Span bgra, Span image, Span scratch) + public static void ColorSpaceTransform(int width, int height, int bits, uint quality, Span bgra, Span image, Span scratch) { int maxTileSize = 1 << bits; int tileXSize = LosslessUtils.SubSampleSize(width, bits); @@ -233,7 +235,7 @@ internal static unsafe class PredictorEncoder Span maxDiffs = MemoryMarshal.Cast(currentRow[(width + 1)..]); float bestDiff = MaxDiffCost; int bestMode = 0; - uint[] residuals = new uint[1 << WebpConstants.MaxTransformBits]; + Span residuals = stackalloc uint[1 << WebpConstants.MaxTransformBits]; // 256 bytes for (int i = 0; i < 4; i++) { histoArgb[i].AsSpan().Clear(); @@ -299,9 +301,7 @@ internal static unsafe class PredictorEncoder if (curDiff < bestDiff) { - int[][] tmp = histoArgb; - histoArgb = bestHisto; - bestHisto = tmp; + (bestHisto, histoArgb) = (histoArgb, bestHisto); bestDiff = curDiff; bestMode = mode; } @@ -837,7 +837,7 @@ internal static unsafe class PredictorEncoder int bits, Vp8LMultipliers prevX, Vp8LMultipliers prevY, - int quality, + uint quality, int xSize, int ySize, int[] accumulatedRedHisto, @@ -871,14 +871,14 @@ internal static unsafe class PredictorEncoder int tileHeight, Vp8LMultipliers prevX, Vp8LMultipliers prevY, - int quality, + uint quality, int[] accumulatedRedHisto, ref Vp8LMultipliers bestTx) { - int maxIters = 4 + ((7 * quality) >> 8); // in range [4..6] + uint maxIters = 4 + ((7 * quality) / 256); // in range [4..6] int greenToRedBest = 0; double bestDiff = GetPredictionCostCrossColorRed(argb, stride, scratch, tileWidth, tileHeight, prevX, prevY, greenToRedBest, accumulatedRedHisto); - for (int iter = 0; iter < maxIters; iter++) + for (int iter = 0; iter < (int)maxIters; iter++) { // ColorTransformDelta is a 3.5 bit fixed point, so 32 is equal to // one in color computation. Having initial delta here as 1 is sufficient @@ -901,7 +901,7 @@ internal static unsafe class PredictorEncoder bestTx.GreenToRed = (byte)(greenToRedBest & 0xff); } - private static void GetBestGreenRedToBlue(Span argb, int stride, Span scratch, int tileWidth, int tileHeight, Vp8LMultipliers prevX, Vp8LMultipliers prevY, int quality, int[] accumulatedBlueHisto, ref Vp8LMultipliers bestTx) + private static void GetBestGreenRedToBlue(Span argb, int stride, Span scratch, int tileWidth, int tileHeight, Vp8LMultipliers prevX, Vp8LMultipliers prevY, uint quality, int[] accumulatedBlueHisto, ref Vp8LMultipliers bestTx) { int iters = (quality < 25) ? 1 : (quality > 50) ? GreenRedToBlueMaxIters : 4; int greenToBlueBest = 0; diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index e714a77253..1f7c7586eb 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -23,7 +23,7 @@ internal class Vp8LEncoder : IDisposable /// /// Scratch buffer to reduce allocations. /// - private readonly int[] scratch = new int[256]; + private ScratchBuffer scratch; // mutable struct, don't make readonly private readonly int[][] histoArgb = { new int[256], new int[256], new int[256], new int[256] }; @@ -57,7 +57,7 @@ internal class Vp8LEncoder : IDisposable /// /// The quality, that will be used to encode the image. /// - private readonly int quality; + private readonly uint quality; /// /// Quality/speed trade-off (0=fast, 6=slower-better). @@ -110,7 +110,7 @@ internal class Vp8LEncoder : IDisposable Configuration configuration, int width, int height, - int quality, + uint quality, bool skipMetadata, WebpEncodingMethod method, WebpTransparentColorMode transparentColorMode, @@ -122,7 +122,7 @@ internal class Vp8LEncoder : IDisposable this.memoryAllocator = memoryAllocator; this.configuration = configuration; - this.quality = Numerics.Clamp(quality, 0, 100); + this.quality = Math.Min(quality, 100u); this.skipMetadata = skipMetadata; this.method = method; this.transparentColorMode = transparentColorMode; @@ -549,12 +549,8 @@ internal class Vp8LEncoder : IDisposable // bgra data with transformations applied. Span bgra = this.EncodedData.GetSpan(); int histogramImageXySize = LosslessUtils.SubSampleSize(width, this.HistoBits) * LosslessUtils.SubSampleSize(height, this.HistoBits); - ushort[] histogramSymbols = new ushort[histogramImageXySize]; - HuffmanTree[] huffTree = new HuffmanTree[3 * WebpConstants.CodeLengthCodes]; - for (int i = 0; i < huffTree.Length; i++) - { - huffTree[i] = default; - } + Span histogramSymbols = histogramImageXySize <= 64 ? stackalloc ushort[histogramImageXySize] : new ushort[histogramImageXySize]; + Span huffTree = stackalloc HuffmanTree[3 * WebpConstants.CodeLengthCodes]; if (useCache) { @@ -607,10 +603,6 @@ internal class Vp8LEncoder : IDisposable int histogramImageSize = histogramImage.Count; int bitArraySize = 5 * histogramImageSize; HuffmanTreeCode[] huffmanCodes = new HuffmanTreeCode[bitArraySize]; - for (int i = 0; i < huffmanCodes.Length; i++) - { - huffmanCodes[i] = default; - } GetHuffBitLengthsAndCodes(histogramImage, huffmanCodes); @@ -702,7 +694,7 @@ internal class Vp8LEncoder : IDisposable /// private void EncodePalette(bool lowEffort) { - Span tmpPalette = new uint[WebpConstants.MaxPaletteSize]; + Span tmpPalette = stackalloc uint[WebpConstants.MaxPaletteSize]; int paletteSize = this.PaletteSize; Span palette = this.Palette.Memory.Span; this.bitWriter.PutBits(WebpConstants.TransformPresent, 1); @@ -763,7 +755,7 @@ internal class Vp8LEncoder : IDisposable int transformWidth = LosslessUtils.SubSampleSize(width, colorTransformBits); int transformHeight = LosslessUtils.SubSampleSize(height, colorTransformBits); - PredictorEncoder.ColorSpaceTransform(width, height, colorTransformBits, this.quality, this.EncodedData.GetSpan(), this.TransformData.GetSpan(), this.scratch); + PredictorEncoder.ColorSpaceTransform(width, height, colorTransformBits, this.quality, this.EncodedData.GetSpan(), this.TransformData.GetSpan(), this.scratch.Span); this.bitWriter.PutBits(WebpConstants.TransformPresent, 1); this.bitWriter.PutBits((uint)Vp8LTransformType.CrossColorTransform, 2); @@ -772,22 +764,13 @@ internal class Vp8LEncoder : IDisposable this.EncodeImageNoHuffman(this.TransformData.GetSpan(), this.HashChain, this.Refs[0], this.Refs[1], transformWidth, transformHeight, this.quality, lowEffort); } - private void EncodeImageNoHuffman(Span bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs refsTmp1, Vp8LBackwardRefs refsTmp2, int width, int height, int quality, bool lowEffort) + private void EncodeImageNoHuffman(Span bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs refsTmp1, Vp8LBackwardRefs refsTmp2, int width, int height, uint quality, bool lowEffort) { int cacheBits = 0; ushort[] histogramSymbols = new ushort[1]; // Only one tree, one symbol. HuffmanTreeCode[] huffmanCodes = new HuffmanTreeCode[5]; - for (int i = 0; i < huffmanCodes.Length; i++) - { - huffmanCodes[i] = default; - } - - HuffmanTree[] huffTree = new HuffmanTree[3UL * WebpConstants.CodeLengthCodes]; - for (int i = 0; i < huffTree.Length; i++) - { - huffTree[i] = default; - } + Span huffTree = stackalloc HuffmanTree[3 * WebpConstants.CodeLengthCodes]; // Calculate backward references from the image pixels. hashChain.Fill(bgra, quality, width, height, lowEffort); @@ -847,10 +830,10 @@ internal class Vp8LEncoder : IDisposable this.StoreImageToBitMask(width, 0, refs, histogramSymbols, huffmanCodes); } - private void StoreHuffmanCode(HuffmanTree[] huffTree, HuffmanTreeToken[] tokens, HuffmanTreeCode huffmanCode) + private void StoreHuffmanCode(Span huffTree, HuffmanTreeToken[] tokens, HuffmanTreeCode huffmanCode) { int count = 0; - Span symbols = this.scratch.AsSpan(0, 2); + Span symbols = this.scratch.Span.Slice(0, 2); symbols.Clear(); const int maxBits = 8; const int maxSymbol = 1 << maxBits; @@ -901,7 +884,7 @@ internal class Vp8LEncoder : IDisposable } } - private void StoreFullHuffmanCode(HuffmanTree[] huffTree, HuffmanTreeToken[] tokens, HuffmanTreeCode tree) + private void StoreFullHuffmanCode(Span huffTree, HuffmanTreeToken[] tokens, HuffmanTreeCode tree) { int i; byte[] codeLengthBitDepth = new byte[WebpConstants.CodeLengthCodes]; @@ -963,7 +946,7 @@ internal class Vp8LEncoder : IDisposable else { int nBits = BitOperations.Log2((uint)trimmedLength - 2); - int nBitPairs = (nBits / 2) + 1; + int nBitPairs = (int)(((uint)nBits / 2) + 1); this.bitWriter.PutBits((uint)nBitPairs - 1, 3); this.bitWriter.PutBits((uint)trimmedLength - 2, nBitPairs * 2); } @@ -1013,7 +996,7 @@ internal class Vp8LEncoder : IDisposable } } - private void StoreImageToBitMask(int width, int histoBits, Vp8LBackwardRefs backwardRefs, ushort[] histogramSymbols, HuffmanTreeCode[] huffmanCodes) + private void StoreImageToBitMask(int width, int histoBits, Vp8LBackwardRefs backwardRefs, Span histogramSymbols, HuffmanTreeCode[] huffmanCodes) { int histoXSize = histoBits > 0 ? LosslessUtils.SubSampleSize(width, histoBits) : 1; int tileMask = histoBits == 0 ? 0 : -(1 << histoBits); @@ -1143,8 +1126,8 @@ internal class Vp8LEncoder : IDisposable prevRow = currentRow; } - double[] entropyComp = new double[(int)HistoIx.HistoTotal]; - double[] entropy = new double[(int)EntropyIx.NumEntropyIx]; + Span entropyComp = stackalloc double[(int)HistoIx.HistoTotal]; + Span entropy = stackalloc double[(int)EntropyIx.NumEntropyIx]; int lastModeToAnalyze = usePalette ? (int)EntropyIx.Palette : (int)EntropyIx.SpatialSubGreen; // Let's add one zero to the predicted histograms. The zeros are removed @@ -1647,11 +1630,7 @@ internal class Vp8LEncoder : IDisposable // Create Huffman trees. bool[] bufRle = new bool[maxNumSymbols]; - HuffmanTree[] huffTree = new HuffmanTree[3 * maxNumSymbols]; - for (int i = 0; i < huffTree.Length; i++) - { - huffTree[i] = default; - } + Span huffTree = stackalloc HuffmanTree[3 * maxNumSymbols]; for (int i = 0; i < histogramImage.Count; i++) { @@ -1849,4 +1828,15 @@ internal class Vp8LEncoder : IDisposable this.TransformData.Dispose(); this.HashChain.Dispose(); } + + /// + /// Scratch buffer to reduce allocations. + /// + private unsafe struct ScratchBuffer + { + private const int Size = 256; + private fixed int scratch[Size]; + + public Span Span => MemoryMarshal.CreateSpan(ref this.scratch[0], Size); + } } diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LHashChain.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LHashChain.cs index 527242906b..3b3864a49d 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LHashChain.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LHashChain.cs @@ -56,7 +56,7 @@ internal sealed class Vp8LHashChain : IDisposable /// public int Size { get; } - public void Fill(ReadOnlySpan bgra, int quality, int xSize, int ySize, bool lowEffort) + public void Fill(ReadOnlySpan bgra, uint quality, int xSize, int ySize, bool lowEffort) { int size = xSize * ySize; int iterMax = GetMaxItersForQuality(quality); @@ -272,14 +272,14 @@ internal sealed class Vp8LHashChain : IDisposable /// The quality. /// Number of hash chain lookups. [MethodImpl(InliningOptions.ShortMethod)] - private static int GetMaxItersForQuality(int quality) => 8 + (quality * quality / 128); + private static int GetMaxItersForQuality(uint quality) => (int)(8 + (quality * quality / 128)); [MethodImpl(InliningOptions.ShortMethod)] - private static int GetWindowSizeForHashChain(int quality, int xSize) + private static int GetWindowSizeForHashChain(uint quality, int xSize) { - int maxWindowSize = quality > 75 ? WindowSize - : quality > 50 ? xSize << 8 - : quality > 25 ? xSize << 6 + int maxWindowSize = quality > 75u ? WindowSize + : quality > 50u ? xSize << 8 + : quality > 25u ? xSize << 6 : xSize << 4; return maxWindowSize > WindowSize ? WindowSize : maxWindowSize; diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs index a4bb2168d2..5ec3f0d53d 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs @@ -513,33 +513,36 @@ internal sealed class Vp8LHistogram : IDeepCloneable DebugGuard.MustBeGreaterThanOrEqualTo(b.Length, count, nameof(b.Length)); DebugGuard.MustBeGreaterThanOrEqualTo(output.Length, count, nameof(output.Length)); - if (Avx2.IsSupported) + if (Avx2.IsSupported && count >= 32) { ref uint aRef = ref MemoryMarshal.GetReference(a); ref uint bRef = ref MemoryMarshal.GetReference(b); ref uint outputRef = ref MemoryMarshal.GetReference(output); - int i; - for (i = 0; i + 32 <= count; i += 32) + nuint idx = 0; + do { // Load values. - Vector256 a0 = Unsafe.As>(ref Unsafe.Add(ref aRef, i)); - Vector256 a1 = Unsafe.As>(ref Unsafe.Add(ref aRef, i + 8)); - Vector256 a2 = Unsafe.As>(ref Unsafe.Add(ref aRef, i + 16)); - Vector256 a3 = Unsafe.As>(ref Unsafe.Add(ref aRef, i + 24)); - Vector256 b0 = Unsafe.As>(ref Unsafe.Add(ref bRef, i)); - Vector256 b1 = Unsafe.As>(ref Unsafe.Add(ref bRef, i + 8)); - Vector256 b2 = Unsafe.As>(ref Unsafe.Add(ref bRef, i + 16)); - Vector256 b3 = Unsafe.As>(ref Unsafe.Add(ref bRef, i + 24)); + Vector256 a0 = Unsafe.As>(ref Unsafe.Add(ref aRef, idx + 0)); + Vector256 a1 = Unsafe.As>(ref Unsafe.Add(ref aRef, idx + 8)); + Vector256 a2 = Unsafe.As>(ref Unsafe.Add(ref aRef, idx + 16)); + Vector256 a3 = Unsafe.As>(ref Unsafe.Add(ref aRef, idx + 24)); + Vector256 b0 = Unsafe.As>(ref Unsafe.Add(ref bRef, idx + 0)); + Vector256 b1 = Unsafe.As>(ref Unsafe.Add(ref bRef, idx + 8)); + Vector256 b2 = Unsafe.As>(ref Unsafe.Add(ref bRef, idx + 16)); + Vector256 b3 = Unsafe.As>(ref Unsafe.Add(ref bRef, idx + 24)); // Note we are adding uint32_t's as *signed* int32's (using _mm_add_epi32). But // that's ok since the histogram values are less than 1<<28 (max picture count). - Unsafe.As>(ref Unsafe.Add(ref outputRef, i)) = Avx2.Add(a0, b0); - Unsafe.As>(ref Unsafe.Add(ref outputRef, i + 8)) = Avx2.Add(a1, b1); - Unsafe.As>(ref Unsafe.Add(ref outputRef, i + 16)) = Avx2.Add(a2, b2); - Unsafe.As>(ref Unsafe.Add(ref outputRef, i + 24)) = Avx2.Add(a3, b3); + Unsafe.As>(ref Unsafe.Add(ref outputRef, idx + 0)) = Avx2.Add(a0, b0); + Unsafe.As>(ref Unsafe.Add(ref outputRef, idx + 8)) = Avx2.Add(a1, b1); + Unsafe.As>(ref Unsafe.Add(ref outputRef, idx + 16)) = Avx2.Add(a2, b2); + Unsafe.As>(ref Unsafe.Add(ref outputRef, idx + 24)) = Avx2.Add(a3, b3); + idx += 32; } + while (idx <= (uint)count - 32); + int i = (int)idx; for (; i < count; i++) { output[i] = a[i] + b[i]; diff --git a/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs b/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs index abfb67bc7e..19ea424199 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs @@ -158,10 +158,9 @@ internal sealed class WebpLosslessDecoder // Finish setting up the color-cache. if (isColorCachePresent) { - decoder.Metadata.ColorCache = new ColorCache(); + decoder.Metadata.ColorCache = new ColorCache(colorCacheBits); colorCacheSize = 1 << colorCacheBits; decoder.Metadata.ColorCacheSize = colorCacheSize; - decoder.Metadata.ColorCache.Init(colorCacheBits); } else { @@ -499,10 +498,7 @@ internal sealed class WebpLosslessDecoder private int ReadHuffmanCode(int alphabetSize, int[] codeLengths, Span table) { bool simpleCode = this.bitReader.ReadBit(); - for (int i = 0; i < alphabetSize; i++) - { - codeLengths[i] = 0; - } + codeLengths.AsSpan(0, alphabetSize).Clear(); if (simpleCode) { diff --git a/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs b/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs index 4756dea867..de3f1586af 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs @@ -5,6 +5,7 @@ using System.Buffers.Binary; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; // ReSharper disable InconsistentNaming @@ -14,7 +15,7 @@ internal static class LossyUtils { // Note: method name in libwebp reference implementation is called VP8SSE16x16. [MethodImpl(InliningOptions.ShortMethod)] - public static int Vp8_Sse16X16(Span a, Span b) + public static int Vp8_Sse16x16(Span a, Span b) { if (Avx2.IsSupported) { @@ -26,12 +27,17 @@ internal static class LossyUtils return Vp8_Sse16xN_Sse2(a, b, 8); } + if (AdvSimd.IsSupported) + { + return Vp8_Sse16x16_Neon(a, b); + } + return Vp8_SseNxN(a, b, 16, 16); } // Note: method name in libwebp reference implementation is called VP8SSE16x8. [MethodImpl(InliningOptions.ShortMethod)] - public static int Vp8_Sse16X8(Span a, Span b) + public static int Vp8_Sse16x8(Span a, Span b) { if (Avx2.IsSupported) { @@ -43,12 +49,17 @@ internal static class LossyUtils return Vp8_Sse16xN_Sse2(a, b, 4); } + if (AdvSimd.IsSupported) + { + return Vp8_Sse16x8_Neon(a, b); + } + return Vp8_SseNxN(a, b, 16, 8); } // Note: method name in libwebp reference implementation is called VP8SSE4x4. [MethodImpl(InliningOptions.ShortMethod)] - public static int Vp8_Sse4X4(Span a, Span b) + public static int Vp8_Sse4x4(Span a, Span b) { if (Avx2.IsSupported) { @@ -77,8 +88,8 @@ internal static class LossyUtils Vector256 b01s = Avx2.UnpackLow(b01.AsByte(), Vector256.Zero); // subtract, square and accumulate. - Vector256 d0 = Avx2.SubtractSaturate(a01s, b01s); - Vector256 e0 = Avx2.MultiplyAddAdjacent(d0.AsInt16(), d0.AsInt16()); + Vector256 d0 = Avx2.SubtractSaturate(a01s.AsInt16(), b01s.AsInt16()); + Vector256 e0 = Avx2.MultiplyAddAdjacent(d0, d0); return Numerics.ReduceSum(e0); } @@ -110,15 +121,20 @@ internal static class LossyUtils Vector128 b23s = Sse2.UnpackLow(b23.AsByte(), Vector128.Zero); // subtract, square and accumulate. - Vector128 d0 = Sse2.SubtractSaturate(a01s, b01s); - Vector128 d1 = Sse2.SubtractSaturate(a23s, b23s); - Vector128 e0 = Sse2.MultiplyAddAdjacent(d0.AsInt16(), d0.AsInt16()); - Vector128 e1 = Sse2.MultiplyAddAdjacent(d1.AsInt16(), d1.AsInt16()); + Vector128 d0 = Sse2.SubtractSaturate(a01s.AsInt16(), b01s.AsInt16()); + Vector128 d1 = Sse2.SubtractSaturate(a23s.AsInt16(), b23s.AsInt16()); + Vector128 e0 = Sse2.MultiplyAddAdjacent(d0, d0); + Vector128 e1 = Sse2.MultiplyAddAdjacent(d1, d1); Vector128 sum = Sse2.Add(e0, e1); return Numerics.ReduceSum(sum); } + if (AdvSimd.IsSupported) + { + return Vp8_Sse4x4_Neon(a, b); + } + return Vp8_SseNxN(a, b, 4, 4); } @@ -126,18 +142,16 @@ internal static class LossyUtils public static int Vp8_SseNxN(Span a, Span b, int w, int h) { int count = 0; - int aOffset = 0; - int bOffset = 0; + int offset = 0; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { - int diff = a[aOffset + x] - b[bOffset + x]; + int diff = a[offset + x] - b[offset + x]; count += diff * diff; } - aOffset += WebpConstants.Bps; - bOffset += WebpConstants.Bps; + offset += WebpConstants.Bps; } return count; @@ -147,7 +161,7 @@ internal static class LossyUtils private static int Vp8_Sse16xN_Sse2(Span a, Span b, int numPairs) { Vector128 sum = Vector128.Zero; - nint offset = 0; + nuint offset = 0; ref byte aRef = ref MemoryMarshal.GetReference(a); ref byte bRef = ref MemoryMarshal.GetReference(b); for (int i = 0; i < numPairs; i++) @@ -172,7 +186,7 @@ internal static class LossyUtils private static int Vp8_Sse16xN_Avx2(Span a, Span b, int numPairs) { Vector256 sum = Vector256.Zero; - nint offset = 0; + nuint offset = 0; ref byte aRef = ref MemoryMarshal.GetReference(a); ref byte bRef = ref MemoryMarshal.GetReference(b); for (int i = 0; i < numPairs; i++) @@ -201,6 +215,106 @@ internal static class LossyUtils return Numerics.ReduceSum(sum); } + [MethodImpl(InliningOptions.ShortMethod)] + private static unsafe int Vp8_Sse16x16_Neon(Span a, Span b) + { + Vector128 sum = Vector128.Zero; + fixed (byte* aRef = &MemoryMarshal.GetReference(a)) + { + fixed (byte* bRef = &MemoryMarshal.GetReference(b)) + { + for (int y = 0; y < 16; y++) + { + sum = AccumulateSSE16Neon(aRef + (y * WebpConstants.Bps), bRef + (y * WebpConstants.Bps), sum); + } + } + } + +#if NET7_0_OR_GREATER + return (int)Vector128.Sum(sum); +#else + return Numerics.ReduceSumArm(sum); +#endif + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static unsafe int Vp8_Sse16x8_Neon(Span a, Span b) + { + Vector128 sum = Vector128.Zero; + fixed (byte* aRef = &MemoryMarshal.GetReference(a)) + { + fixed (byte* bRef = &MemoryMarshal.GetReference(b)) + { + for (int y = 0; y < 8; y++) + { + sum = AccumulateSSE16Neon(aRef + (y * WebpConstants.Bps), bRef + (y * WebpConstants.Bps), sum); + } + } + } + +#if NET7_0_OR_GREATER + return (int)Vector128.Sum(sum); +#else + return Numerics.ReduceSumArm(sum); +#endif + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static int Vp8_Sse4x4_Neon(Span a, Span b) + { + Vector128 a0 = Load4x4Neon(a).AsByte(); + Vector128 b0 = Load4x4Neon(b).AsByte(); + Vector128 absDiff = AdvSimd.AbsoluteDifference(a0, b0); + Vector64 absDiffLower = absDiff.GetLower().AsByte(); + Vector64 absDiffUpper = absDiff.GetUpper().AsByte(); + Vector128 prod1 = AdvSimd.MultiplyWideningLower(absDiffLower, absDiffLower); + Vector128 prod2 = AdvSimd.MultiplyWideningLower(absDiffUpper, absDiffUpper); + + // pair-wise adds and widen. + Vector128 sum1 = AdvSimd.AddPairwiseWidening(prod1); + Vector128 sum2 = AdvSimd.AddPairwiseWidening(prod2); + + Vector128 sum = AdvSimd.Add(sum1, sum2); +#if NET7_0_OR_GREATER + return (int)Vector128.Sum(sum); +#else + return Numerics.ReduceSumArm(sum); +#endif + } + + // Load all 4x4 pixels into a single Vector128 + [MethodImpl(InliningOptions.ShortMethod)] + private static unsafe Vector128 Load4x4Neon(Span src) + { + fixed (byte* srcRef = &MemoryMarshal.GetReference(src)) + { + Vector128 output = Vector128.Zero; + output = AdvSimd.LoadAndInsertScalar(output, 0, (uint*)srcRef); + output = AdvSimd.LoadAndInsertScalar(output, 1, (uint*)(srcRef + WebpConstants.Bps)); + output = AdvSimd.LoadAndInsertScalar(output, 2, (uint*)(srcRef + (WebpConstants.Bps * 2))); + output = AdvSimd.LoadAndInsertScalar(output, 3, (uint*)(srcRef + (WebpConstants.Bps * 3))); + return output; + } + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static unsafe Vector128 AccumulateSSE16Neon(byte* a, byte* b, Vector128 sum) + { + Vector128 a0 = AdvSimd.LoadVector128(a); + Vector128 b0 = AdvSimd.LoadVector128(b); + + Vector128 absDiff = AdvSimd.AbsoluteDifference(a0, b0); + Vector64 absDiffLower = absDiff.GetLower(); + Vector64 absDiffUpper = absDiff.GetUpper(); + Vector128 prod1 = AdvSimd.MultiplyWideningLower(absDiffLower, absDiffLower); + Vector128 prod2 = AdvSimd.MultiplyWideningLower(absDiffUpper, absDiffUpper); + + // pair-wise adds and widen. + Vector128 sum1 = AdvSimd.AddPairwiseWidening(prod1); + Vector128 sum2 = AdvSimd.AddPairwiseWidening(prod2); + return AdvSimd.Add(sum, AdvSimd.Add(sum1, sum2)); + } + [MethodImpl(InliningOptions.ShortMethod)] private static Vector128 SubtractAndAccumulate(Vector128 a, Vector128 b) { @@ -1313,17 +1427,17 @@ internal static class LossyUtils if (Sse2.IsSupported) { // Load. - ref byte pRef = ref Unsafe.Add(ref MemoryMarshal.GetReference(p), offset); + ref byte pRef = ref Unsafe.Add(ref MemoryMarshal.GetReference(p), (uint)offset); Vector128 p1 = Unsafe.As>(ref Unsafe.Subtract(ref pRef, 2 * stride)); Vector128 p0 = Unsafe.As>(ref Unsafe.Subtract(ref pRef, stride)); Vector128 q0 = Unsafe.As>(ref pRef); - Vector128 q1 = Unsafe.As>(ref Unsafe.Add(ref pRef, stride)); + Vector128 q1 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)stride)); DoFilter2Sse2(ref p1, ref p0, ref q0, ref q1, thresh); // Store. - ref byte outputRef = ref Unsafe.Add(ref MemoryMarshal.GetReference(p), offset); + ref byte outputRef = ref Unsafe.Add(ref MemoryMarshal.GetReference(p), (uint)offset); Unsafe.As>(ref Unsafe.Subtract(ref outputRef, stride)) = p0.AsSByte(); Unsafe.As>(ref outputRef) = q0.AsSByte(); } @@ -1346,11 +1460,11 @@ internal static class LossyUtils if (Sse2.IsSupported) { // Beginning of p1 - ref byte pRef = ref Unsafe.Add(ref MemoryMarshal.GetReference(p), offset - 2); + ref byte pRef = ref Unsafe.Add(ref MemoryMarshal.GetReference(p), (uint)(offset - 2)); - Load16x4(ref pRef, ref Unsafe.Add(ref pRef, 8 * stride), stride, out Vector128 p1, out Vector128 p0, out Vector128 q0, out Vector128 q1); + Load16x4(ref pRef, ref Unsafe.Add(ref pRef, 8 * (uint)stride), stride, out Vector128 p1, out Vector128 p0, out Vector128 q0, out Vector128 q1); DoFilter2Sse2(ref p1, ref p0, ref q0, ref q1, thresh); - Store16x4(p1, p0, q0, q1, ref pRef, ref Unsafe.Add(ref pRef, 8 * stride), stride); + Store16x4(p1, p0, q0, q1, ref pRef, ref Unsafe.Add(ref pRef, 8 * (uint)stride), stride); } else { @@ -1413,19 +1527,19 @@ internal static class LossyUtils if (Sse2.IsSupported) { ref byte pRef = ref MemoryMarshal.GetReference(p); - Vector128 t1 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset - (4 * stride))); - Vector128 p2 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset - (3 * stride))); - Vector128 p1 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset - (2 * stride))); - Vector128 p0 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset - stride)); + Vector128 t1 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset - (4 * stride)))); + Vector128 p2 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset - (3 * stride)))); + Vector128 p1 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset - (2 * stride)))); + Vector128 p0 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset - stride))); Vector128 mask = Abs(p1, p0); mask = Sse2.Max(mask, Abs(t1, p2)); mask = Sse2.Max(mask, Abs(p2, p1)); - Vector128 q0 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset)); - Vector128 q1 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset + stride)); - Vector128 q2 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset + (2 * stride))); - t1 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset + (3 * stride))); + Vector128 q0 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)offset)); + Vector128 q1 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset + stride))); + Vector128 q2 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset + (2 * stride)))); + t1 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset + (3 * stride)))); mask = Sse2.Max(mask, Abs(q1, q0)); mask = Sse2.Max(mask, Abs(t1, q2)); @@ -1436,12 +1550,12 @@ internal static class LossyUtils // Store. ref byte outputRef = ref MemoryMarshal.GetReference(p); - Unsafe.As>(ref Unsafe.Add(ref outputRef, offset - (3 * stride))) = p2.AsInt32(); - Unsafe.As>(ref Unsafe.Add(ref outputRef, offset - (2 * stride))) = p1.AsInt32(); - Unsafe.As>(ref Unsafe.Add(ref outputRef, offset - stride)) = p0.AsInt32(); - Unsafe.As>(ref Unsafe.Add(ref outputRef, offset)) = q0.AsInt32(); - Unsafe.As>(ref Unsafe.Add(ref outputRef, offset + stride)) = q1.AsInt32(); - Unsafe.As>(ref Unsafe.Add(ref outputRef, offset + (2 * stride))) = q2.AsInt32(); + Unsafe.As>(ref Unsafe.Add(ref outputRef, (uint)(offset - (3 * stride)))) = p2.AsInt32(); + Unsafe.As>(ref Unsafe.Add(ref outputRef, (uint)(offset - (2 * stride)))) = p1.AsInt32(); + Unsafe.As>(ref Unsafe.Add(ref outputRef, (uint)(offset - stride))) = p0.AsInt32(); + Unsafe.As>(ref Unsafe.Add(ref outputRef, (uint)offset)) = q0.AsInt32(); + Unsafe.As>(ref Unsafe.Add(ref outputRef, (uint)(offset + stride))) = q1.AsInt32(); + Unsafe.As>(ref Unsafe.Add(ref outputRef, (uint)(offset + (2 * stride)))) = q2.AsInt32(); } else { @@ -1455,14 +1569,14 @@ internal static class LossyUtils if (Sse2.IsSupported) { ref byte pRef = ref MemoryMarshal.GetReference(p); - ref byte bRef = ref Unsafe.Add(ref pRef, offset - 4); - Load16x4(ref bRef, ref Unsafe.Add(ref bRef, 8 * stride), stride, out Vector128 p3, out Vector128 p2, out Vector128 p1, out Vector128 p0); + ref byte bRef = ref Unsafe.Add(ref pRef, (uint)offset - 4); + Load16x4(ref bRef, ref Unsafe.Add(ref bRef, 8 * (uint)stride), stride, out Vector128 p3, out Vector128 p2, out Vector128 p1, out Vector128 p0); Vector128 mask = Abs(p1, p0); mask = Sse2.Max(mask, Abs(p3, p2)); mask = Sse2.Max(mask, Abs(p2, p1)); - Load16x4(ref Unsafe.Add(ref pRef, offset), ref Unsafe.Add(ref pRef, offset + (8 * stride)), stride, out Vector128 q0, out Vector128 q1, out Vector128 q2, out Vector128 q3); + Load16x4(ref Unsafe.Add(ref pRef, (uint)offset), ref Unsafe.Add(ref pRef, (uint)(offset + (8 * stride))), stride, out Vector128 q0, out Vector128 q1, out Vector128 q2, out Vector128 q3); mask = Sse2.Max(mask, Abs(q1, q0)); mask = Sse2.Max(mask, Abs(q3, q2)); @@ -1471,8 +1585,8 @@ internal static class LossyUtils ComplexMask(p1, p0, q0, q1, thresh, ithresh, ref mask); DoFilter6Sse2(ref p2, ref p1, ref p0, ref q0, ref q1, ref q2, mask, hevThresh); - Store16x4(p3, p2, p1, p0, ref bRef, ref Unsafe.Add(ref bRef, 8 * stride), stride); - Store16x4(q0, q1, q2, q3, ref Unsafe.Add(ref pRef, offset), ref Unsafe.Add(ref pRef, offset + (8 * stride)), stride); + Store16x4(p3, p2, p1, p0, ref bRef, ref Unsafe.Add(ref bRef, 8 * (uint)stride), stride); + Store16x4(q0, q1, q2, q3, ref Unsafe.Add(ref pRef, (uint)offset), ref Unsafe.Add(ref pRef, (uint)(offset + (8 * stride))), stride); } else { @@ -1485,10 +1599,10 @@ internal static class LossyUtils if (Sse2.IsSupported) { ref byte pRef = ref MemoryMarshal.GetReference(p); - Vector128 p3 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset)); - Vector128 p2 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset + stride)); - Vector128 p1 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset + (2 * stride))); - Vector128 p0 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset + (3 * stride))); + Vector128 p3 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)offset)); + Vector128 p2 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset + stride))); + Vector128 p1 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset + (2 * stride)))); + Vector128 p0 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset + (3 * stride)))); for (int k = 3; k > 0; k--) { @@ -1500,10 +1614,10 @@ internal static class LossyUtils mask = Sse2.Max(mask, Abs(p3, p2)); mask = Sse2.Max(mask, Abs(p2, p1)); - p3 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset)); - p2 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset + stride)); - Vector128 tmp1 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset + (2 * stride))); - Vector128 tmp2 = Unsafe.As>(ref Unsafe.Add(ref pRef, offset + (3 * stride))); + p3 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)offset)); + p2 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset + stride))); + Vector128 tmp1 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset + (2 * stride)))); + Vector128 tmp2 = Unsafe.As>(ref Unsafe.Add(ref pRef, (uint)(offset + (3 * stride)))); mask = Sse2.Max(mask, Abs(tmp1, tmp2)); mask = Sse2.Max(mask, Abs(p3, p2)); @@ -1517,9 +1631,9 @@ internal static class LossyUtils // Store. ref byte outputRef = ref MemoryMarshal.GetReference(b); Unsafe.As>(ref outputRef) = p1.AsInt32(); - Unsafe.As>(ref Unsafe.Add(ref outputRef, stride)) = p0.AsInt32(); - Unsafe.As>(ref Unsafe.Add(ref outputRef, stride * 2)) = p3.AsInt32(); - Unsafe.As>(ref Unsafe.Add(ref outputRef, stride * 3)) = p2.AsInt32(); + Unsafe.As>(ref Unsafe.Add(ref outputRef, (uint)stride)) = p0.AsInt32(); + Unsafe.As>(ref Unsafe.Add(ref outputRef, (uint)(stride * 2))) = p3.AsInt32(); + Unsafe.As>(ref Unsafe.Add(ref outputRef, (uint)(stride * 3))) = p2.AsInt32(); // Rotate samples. p1 = tmp1; @@ -1541,13 +1655,13 @@ internal static class LossyUtils if (Sse2.IsSupported) { ref byte pRef = ref MemoryMarshal.GetReference(p); - Load16x4(ref Unsafe.Add(ref pRef, offset), ref Unsafe.Add(ref pRef, offset + (8 * stride)), stride, out Vector128 p3, out Vector128 p2, out Vector128 p1, out Vector128 p0); + Load16x4(ref Unsafe.Add(ref pRef, (uint)offset), ref Unsafe.Add(ref pRef, (uint)(offset + (8 * stride))), stride, out Vector128 p3, out Vector128 p2, out Vector128 p1, out Vector128 p0); Vector128 mask; for (int k = 3; k > 0; k--) { // Beginning of p1. - ref byte bRef = ref Unsafe.Add(ref pRef, offset + 2); + ref byte bRef = ref Unsafe.Add(ref pRef, (uint)offset + 2); // Beginning of q0 (and next span). offset += 4; @@ -1557,7 +1671,7 @@ internal static class LossyUtils mask = Sse2.Max(mask, Abs(p3, p2)); mask = Sse2.Max(mask, Abs(p2, p1)); - Load16x4(ref Unsafe.Add(ref pRef, offset), ref Unsafe.Add(ref pRef, offset + (8 * stride)), stride, out p3, out p2, out Vector128 tmp1, out Vector128 tmp2); + Load16x4(ref Unsafe.Add(ref pRef, (uint)offset), ref Unsafe.Add(ref pRef, (uint)(offset + (8 * stride))), stride, out p3, out p2, out Vector128 tmp1, out Vector128 tmp2); mask = Sse2.Max(mask, Abs(tmp1, tmp2)); mask = Sse2.Max(mask, Abs(p3, p2)); @@ -1566,7 +1680,7 @@ internal static class LossyUtils ComplexMask(p1, p0, p3, p2, thresh, ithresh, ref mask); DoFilter4Sse2(ref p1, ref p0, ref p3, ref p2, mask, hevThresh); - Store16x4(p1, p0, p3, p2, ref bRef, ref Unsafe.Add(ref bRef, 8 * stride), stride); + Store16x4(p1, p0, p3, p2, ref bRef, ref Unsafe.Add(ref bRef, 8 * (uint)stride), stride); // Rotate samples. p1 = tmp1; @@ -1635,13 +1749,13 @@ internal static class LossyUtils { ref byte uRef = ref MemoryMarshal.GetReference(u); ref byte vRef = ref MemoryMarshal.GetReference(v); - Load16x4(ref Unsafe.Add(ref uRef, offset - 4), ref Unsafe.Add(ref vRef, offset - 4), stride, out Vector128 p3, out Vector128 p2, out Vector128 p1, out Vector128 p0); + Load16x4(ref Unsafe.Add(ref uRef, (uint)offset - 4), ref Unsafe.Add(ref vRef, (uint)offset - 4), stride, out Vector128 p3, out Vector128 p2, out Vector128 p1, out Vector128 p0); Vector128 mask = Abs(p1, p0); mask = Sse2.Max(mask, Abs(p3, p2)); mask = Sse2.Max(mask, Abs(p2, p1)); - Load16x4(ref Unsafe.Add(ref uRef, offset), ref Unsafe.Add(ref vRef, offset), stride, out Vector128 q0, out Vector128 q1, out Vector128 q2, out Vector128 q3); + Load16x4(ref Unsafe.Add(ref uRef, (uint)offset), ref Unsafe.Add(ref vRef, (uint)offset), stride, out Vector128 q0, out Vector128 q1, out Vector128 q2, out Vector128 q3); mask = Sse2.Max(mask, Abs(q1, q0)); mask = Sse2.Max(mask, Abs(q3, q2)); @@ -1650,8 +1764,8 @@ internal static class LossyUtils ComplexMask(p1, p0, q0, q1, thresh, ithresh, ref mask); DoFilter6Sse2(ref p2, ref p1, ref p0, ref q0, ref q1, ref q2, mask, hevThresh); - Store16x4(p3, p2, p1, p0, ref Unsafe.Add(ref uRef, offset - 4), ref Unsafe.Add(ref vRef, offset - 4), stride); - Store16x4(q0, q1, q2, q3, ref Unsafe.Add(ref uRef, offset), ref Unsafe.Add(ref vRef, offset), stride); + Store16x4(p3, p2, p1, p0, ref Unsafe.Add(ref uRef, (uint)offset - 4), ref Unsafe.Add(ref vRef, (uint)offset - 4), stride); + Store16x4(q0, q1, q2, q3, ref Unsafe.Add(ref uRef, (uint)offset), ref Unsafe.Add(ref vRef, (uint)offset), stride); } else { @@ -1712,7 +1826,7 @@ internal static class LossyUtils { ref byte uRef = ref MemoryMarshal.GetReference(u); ref byte vRef = ref MemoryMarshal.GetReference(v); - Load16x4(ref Unsafe.Add(ref uRef, offset), ref Unsafe.Add(ref vRef, offset), stride, out Vector128 t2, out Vector128 t1, out Vector128 p1, out Vector128 p0); + Load16x4(ref Unsafe.Add(ref uRef, (uint)offset), ref Unsafe.Add(ref vRef, (uint)offset), stride, out Vector128 t2, out Vector128 t1, out Vector128 p1, out Vector128 p0); Vector128 mask = Abs(p1, p0); mask = Sse2.Max(mask, Abs(t2, t1)); @@ -1721,7 +1835,7 @@ internal static class LossyUtils // Beginning of q0. offset += 4; - Load16x4(ref Unsafe.Add(ref uRef, offset), ref Unsafe.Add(ref vRef, offset), stride, out Vector128 q0, out Vector128 q1, out t1, out t2); + Load16x4(ref Unsafe.Add(ref uRef, (uint)offset), ref Unsafe.Add(ref vRef, (uint)offset), stride, out Vector128 q0, out Vector128 q1, out t1, out t2); mask = Sse2.Max(mask, Abs(q1, q0)); mask = Sse2.Max(mask, Abs(t2, t1)); @@ -1732,7 +1846,7 @@ internal static class LossyUtils // Beginning of p1. offset -= 2; - Store16x4(p1, p0, q0, q1, ref Unsafe.Add(ref uRef, offset), ref Unsafe.Add(ref vRef, offset), stride); + Store16x4(p1, p0, q0, q1, ref Unsafe.Add(ref uRef, (uint)offset), ref Unsafe.Add(ref vRef, (uint)offset), stride); } else { @@ -2164,8 +2278,8 @@ internal static class LossyUtils // q0 = 73 63 53 43 33 23 13 03 72 62 52 42 32 22 12 02 // p0 = f1 e1 d1 c1 b1 a1 91 81 f0 e0 d0 c0 b0 a0 90 80 // q1 = f3 e3 d3 c3 b3 a3 93 83 f2 e2 d2 c2 b2 a2 92 82 - Load8x4(ref r0, stride, out Vector128 t1, out Vector128 t2); - Load8x4(ref r8, stride, out p0, out q1); + Load8x4(ref r0, (uint)stride, out Vector128 t1, out Vector128 t2); + Load8x4(ref r8, (uint)stride, out p0, out q1); // p1 = f0 e0 d0 c0 b0 a0 90 80 70 60 50 40 30 20 10 00 // p0 = f1 e1 d1 c1 b1 a1 91 81 71 61 51 41 31 21 11 01 @@ -2178,7 +2292,7 @@ internal static class LossyUtils } // Reads 8 rows across a vertical edge. - private static void Load8x4(ref byte bRef, int stride, out Vector128 p, out Vector128 q) + private static void Load8x4(ref byte bRef, nuint stride, out Vector128 p, out Vector128 q) { // A0 = 63 62 61 60 23 22 21 20 43 42 41 40 03 02 01 00 // A1 = 73 72 71 70 33 32 31 30 53 52 51 50 13 12 11 10 @@ -2235,10 +2349,10 @@ internal static class LossyUtils q1s = Sse2.UnpackHigh(t1.AsInt16(), q1s.AsInt16()).AsByte(); Store4x4(p0s, ref r0Ref, stride); - Store4x4(q0s, ref Unsafe.Add(ref r0Ref, 4 * stride), stride); + Store4x4(q0s, ref Unsafe.Add(ref r0Ref, 4 * (uint)stride), stride); Store4x4(p1s, ref r8Ref, stride); - Store4x4(q1s, ref Unsafe.Add(ref r8Ref, 4 * stride), stride); + Store4x4(q1s, ref Unsafe.Add(ref r8Ref, 4 * (uint)stride), stride); } private static void Store4x4(Vector128 x, ref byte dstRef, int stride) @@ -2246,7 +2360,7 @@ internal static class LossyUtils int offset = 0; for (int i = 0; i < 4; i++) { - Unsafe.As(ref Unsafe.Add(ref dstRef, offset)) = Sse2.ConvertToInt32(x.AsInt32()); + Unsafe.As(ref Unsafe.Add(ref dstRef, (uint)offset)) = Sse2.ConvertToInt32(x.AsInt32()); x = Sse2.ShiftRightLogical128BitLane(x, 4); offset += stride; } @@ -2307,16 +2421,16 @@ internal static class LossyUtils [MethodImpl(InliningOptions.ShortMethod)] private static Vector128 LoadUvEdge(ref byte uRef, ref byte vRef, int offset) { - var uVec = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref uRef, offset)), 0); - var vVec = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref vRef, offset)), 0); + var uVec = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref uRef, (uint)offset)), 0); + var vVec = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref vRef, (uint)offset)), 0); return Sse2.UnpackLow(uVec, vVec).AsByte(); } [MethodImpl(InliningOptions.ShortMethod)] private static void StoreUv(Vector128 x, ref byte uRef, ref byte vRef, int offset) { - Unsafe.As>(ref Unsafe.Add(ref uRef, offset)) = x.GetLower(); - Unsafe.As>(ref Unsafe.Add(ref vRef, offset)) = x.GetUpper(); + Unsafe.As>(ref Unsafe.Add(ref uRef, (uint)offset)) = x.GetLower(); + Unsafe.As>(ref Unsafe.Add(ref vRef, (uint)offset)) = x.GetUpper(); } // Compute abs(p - q) = subs(p - q) OR subs(q - p) diff --git a/src/ImageSharp/Formats/Webp/Lossy/PassStats.cs b/src/ImageSharp/Formats/Webp/Lossy/PassStats.cs index ba554866e3..470c9c1042 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/PassStats.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/PassStats.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy; /// internal class PassStats { - public PassStats(long targetSize, float targetPsnr, int qMin, int qMax, int quality) + public PassStats(long targetSize, float targetPsnr, int qMin, int qMax, uint quality) { bool doSizeSearch = targetSize != 0; diff --git a/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs b/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs index fed9c16d4d..e9eb1110b0 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs @@ -53,7 +53,7 @@ internal static unsafe class QuantEnc rdCur.Nz = (uint)ReconstructIntra16(it, dqm, rdCur, tmpDst, mode); // Measure RD-score. - rdCur.D = LossyUtils.Vp8_Sse16X16(src, tmpDst); + rdCur.D = LossyUtils.Vp8_Sse16x16(src, tmpDst); rdCur.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto16X16(src, tmpDst, WeightY, scratch)) : 0; rdCur.H = WebpConstants.Vp8FixedCostsI16[mode]; rdCur.R = it.GetCostLuma16(rdCur, proba, res); @@ -121,7 +121,7 @@ internal static unsafe class QuantEnc var rdi4 = new Vp8ModeScore(); var rdTmp = new Vp8ModeScore(); var res = new Vp8Residual(); - Span tmpLevels = new short[16]; + Span tmpLevels = stackalloc short[16]; do { const int numBlocks = 1; @@ -145,7 +145,7 @@ internal static unsafe class QuantEnc rdTmp.Nz = (uint)ReconstructIntra4(it, dqm, tmpLevels, src, tmpDst, mode); // Compute RD-score. - rdTmp.D = LossyUtils.Vp8_Sse4X4(src, tmpDst); + rdTmp.D = LossyUtils.Vp8_Sse4x4(src, tmpDst); rdTmp.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto4X4(src, tmpDst, WeightY, scratch)) : 0; rdTmp.H = modeCosts[mode]; @@ -235,7 +235,7 @@ internal static unsafe class QuantEnc rdUv.Nz = (uint)ReconstructUv(it, dqm, rdUv, tmpDst, mode); // Compute RD-score - rdUv.D = LossyUtils.Vp8_Sse16X8(src, tmpDst); + rdUv.D = LossyUtils.Vp8_Sse16x8(src, tmpDst); rdUv.SD = 0; // not calling TDisto here: it tends to flatten areas. rdUv.H = WebpConstants.Vp8FixedCostsUv[mode]; rdUv.R = it.GetCostUv(rdUv, proba, res); @@ -389,7 +389,7 @@ internal static unsafe class QuantEnc for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) { Span reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]); - long score = (LossyUtils.Vp8_Sse16X16(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsI16[mode] * lambdaDi16); + long score = (LossyUtils.Vp8_Sse16x16(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsI16[mode] * lambdaDi16); if (mode > 0 && WebpConstants.Vp8FixedCostsI16[mode] > bitLimit) { @@ -436,7 +436,7 @@ internal static unsafe class QuantEnc for (mode = 0; mode < WebpConstants.NumBModes; ++mode) { Span reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]); - long score = (LossyUtils.Vp8_Sse4X4(src, reference) * WebpConstants.RdDistoMult) + (modeCosts[mode] * lambdaDi4); + long score = (LossyUtils.Vp8_Sse4x4(src, reference) * WebpConstants.RdDistoMult) + (modeCosts[mode] * lambdaDi4); if (score < bestI4Score) { bestI4Mode = mode; @@ -485,7 +485,7 @@ internal static unsafe class QuantEnc for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) { Span reference = it.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]); - long score = (LossyUtils.Vp8_Sse16X8(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsUv[mode] * lambdaDuv); + long score = (LossyUtils.Vp8_Sse16x8(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsUv[mode] * lambdaDuv); if (score < bestUvScore) { bestMode = mode; @@ -770,7 +770,7 @@ internal static unsafe class QuantEnc { uint v = src[0] * 0x01010101u; Span vSpan = BitConverter.GetBytes(v).AsSpan(); - for (nint i = 0; i < 16; i++) + for (nuint i = 0; i < 16; i++) { if (!src[..4].SequenceEqual(vSpan) || !src.Slice(4, 4).SequenceEqual(vSpan) || !src.Slice(8, 4).SequenceEqual(vSpan) || !src.Slice(12, 4).SequenceEqual(vSpan)) @@ -789,10 +789,10 @@ internal static unsafe class QuantEnc { int score = 0; ref short levelsRef = ref MemoryMarshal.GetReference(levels); - int offset = 0; + nuint offset = 0; while (numBlocks-- > 0) { - for (nint i = 1; i < 16; i++) + for (nuint i = 1; i < 16; i++) { // omit DC, we're only interested in AC score += Unsafe.Add(ref levelsRef, offset) != 0 ? 1 : 0; diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs index fa5fe51c79..7211f93766 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs @@ -347,12 +347,12 @@ internal class Vp8EncIterator } } - public int FastMbAnalyze(int quality) + public int FastMbAnalyze(uint quality) { // Empirical cut-off value, should be around 16 (~=block size). We use the // [8-17] range and favor intra4 at high quality, intra16 for low quality. - int q = quality; - int kThreshold = 8 + ((17 - 8) * q / 100); + uint q = quality; + uint kThreshold = 8 + ((17 - 8) * q / 100); int k; Span dc = stackalloc uint[16]; uint m; @@ -374,7 +374,7 @@ internal class Vp8EncIterator } else { - byte[] modes = new byte[16]; // DC4 + Span modes = stackalloc byte[16]; // DC4 this.SetIntra4Mode(modes); } @@ -407,7 +407,7 @@ internal class Vp8EncIterator public int MbAnalyzeBestIntra4Mode(int bestAlpha) { - byte[] modes = new byte[16]; + Span modes = stackalloc byte[16]; const int maxMode = MaxIntra4Mode; Vp8Histogram totalHisto = new(); int curHisto = 0; @@ -494,13 +494,13 @@ internal class Vp8EncIterator this.CurrentMacroBlockInfo.MacroBlockType = Vp8MacroBlockType.I16X16; } - public void SetIntra4Mode(byte[] modes) + public void SetIntra4Mode(ReadOnlySpan modes) { int modesIdx = 0; int predIdx = this.PredIdx; for (int y = 4; y > 0; y--) { - modes.AsSpan(modesIdx, 4).CopyTo(this.Preds.AsSpan(predIdx)); + modes.Slice(modesIdx, 4).CopyTo(this.Preds.AsSpan(predIdx)); predIdx += this.predsWidth; modesIdx += 4; } diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs index a5d1900b2a..f17d965e87 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs @@ -31,7 +31,7 @@ internal class Vp8Encoder : IDisposable /// /// The quality, that will be used to encode the image. /// - private readonly int quality; + private readonly uint quality; /// /// Quality/speed trade-off (0=fast, 6=slower-better). @@ -113,7 +113,7 @@ internal class Vp8Encoder : IDisposable Configuration configuration, int width, int height, - int quality, + uint quality, bool skipMetadata, WebpEncodingMethod method, int entropyPasses, @@ -125,7 +125,7 @@ internal class Vp8Encoder : IDisposable this.configuration = configuration; this.Width = width; this.Height = height; - this.quality = Numerics.Clamp(quality, 0, 100); + this.quality = Math.Min(quality, 100); this.skipMetadata = skipMetadata; this.method = method; this.entropyPasses = Numerics.Clamp(entropyPasses, 1, 10); @@ -329,7 +329,7 @@ internal class Vp8Encoder : IDisposable int uvStride = (yStride + 1) >> 1; Vp8EncIterator it = new(this.YTop, this.UvTop, this.Nz, this.MbInfo, this.Preds, this.TopDerr, this.Mbw, this.Mbh); - int[] alphas = new int[WebpConstants.MaxAlpha + 1]; + Span alphas = stackalloc int[WebpConstants.MaxAlpha + 1]; this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha); int totalMb = this.Mbw * this.Mbw; this.alpha /= totalMb; @@ -345,23 +345,6 @@ internal class Vp8Encoder : IDisposable int expectedSize = this.Mbw * this.Mbh * averageBytesPerMacroBlock; this.bitWriter = new Vp8BitWriter(expectedSize, this); - // Extract and encode alpha channel data, if present. - int alphaDataSize = 0; - bool alphaCompressionSucceeded = false; - using AlphaEncoder alphaEncoder = new(); - Span alphaData = Span.Empty; - if (hasAlpha) - { - // TODO: This can potentially run in an separate task. - IMemoryOwner encodedAlphaData = alphaEncoder.EncodeAlpha(image, this.configuration, this.memoryAllocator, this.skipMetadata, this.alphaCompression, out alphaDataSize); - alphaData = encodedAlphaData.GetSpan(); - if (alphaDataSize < pixelCount) - { - // Only use compressed data, if the compressed data is actually smaller then the uncompressed data. - alphaCompressionSucceeded = true; - } - } - // Stats-collection loop. this.StatLoop(width, height, yStride, uvStride); it.Init(); @@ -399,16 +382,47 @@ internal class Vp8Encoder : IDisposable ExifProfile exifProfile = this.skipMetadata ? null : metadata.ExifProfile; XmpProfile xmpProfile = this.skipMetadata ? null : metadata.XmpProfile; - this.bitWriter.WriteEncodedImageToStream( - stream, - exifProfile, - xmpProfile, - metadata.IccProfile, - (uint)width, - (uint)height, - hasAlpha, - alphaData[..alphaDataSize], - this.alphaCompression && alphaCompressionSucceeded); + // Extract and encode alpha channel data, if present. + int alphaDataSize = 0; + bool alphaCompressionSucceeded = false; + Span alphaData = Span.Empty; + IMemoryOwner encodedAlphaData = null; + try + { + if (hasAlpha) + { + // TODO: This can potentially run in an separate task. + encodedAlphaData = AlphaEncoder.EncodeAlpha( + image, + this.configuration, + this.memoryAllocator, + this.skipMetadata, + this.alphaCompression, + out alphaDataSize); + + alphaData = encodedAlphaData.GetSpan(); + if (alphaDataSize < pixelCount) + { + // Only use compressed data, if the compressed data is actually smaller then the uncompressed data. + alphaCompressionSucceeded = true; + } + } + + this.bitWriter.WriteEncodedImageToStream( + stream, + exifProfile, + xmpProfile, + metadata.IccProfile, + (uint)width, + (uint)height, + hasAlpha, + alphaData[..alphaDataSize], + this.alphaCompression && alphaCompressionSucceeded); + } + finally + { + encodedAlphaData?.Dispose(); + } } /// @@ -611,15 +625,15 @@ internal class Vp8Encoder : IDisposable } // Simplified k-Means, to assign Nb segments based on alpha-histogram. - private void AssignSegments(int[] alphas) + private void AssignSegments(ReadOnlySpan alphas) { int nb = this.SegmentHeader.NumSegments < NumMbSegments ? this.SegmentHeader.NumSegments : NumMbSegments; - int[] centers = new int[NumMbSegments]; + Span centers = stackalloc int[NumMbSegments]; int weightedAverage = 0; - int[] map = new int[WebpConstants.MaxAlpha + 1]; + Span map = stackalloc int[WebpConstants.MaxAlpha + 1]; int n, k; - int[] accum = new int[NumMbSegments]; - int[] distAccum = new int[NumMbSegments]; + Span accum = stackalloc int[NumMbSegments]; + Span distAccum = stackalloc int[NumMbSegments]; // Bracket the input. for (n = 0; n <= WebpConstants.MaxAlpha && alphas[n] == 0; ++n) @@ -677,7 +691,7 @@ internal class Vp8Encoder : IDisposable { if (accum[n] != 0) { - int newCenter = (distAccum[n] + (accum[n] / 2)) / accum[n]; + int newCenter = (distAccum[n] + (accum[n] >> 1)) / accum[n]; // >> 1 is bit-hack for / 2 displaced += Math.Abs(centers[n] - newCenter); centers[n] = newCenter; weightedAverage += newCenter * accum[n]; @@ -685,7 +699,7 @@ internal class Vp8Encoder : IDisposable } } - weightedAverage = (weightedAverage + (totalWeight / 2)) / totalWeight; + weightedAverage = (weightedAverage + (totalWeight >> 1)) / totalWeight; // >> 1 is bit-hack for / 2 if (displaced < 5) { break; // no need to keep on looping... @@ -705,7 +719,7 @@ internal class Vp8Encoder : IDisposable this.SetSegmentAlphas(centers, weightedAverage); } - private void SetSegmentAlphas(int[] centers, int mid) + private void SetSegmentAlphas(ReadOnlySpan centers, int mid) { int nb = this.SegmentHeader.NumSegments; Vp8SegmentInfo[] dqm = this.SegmentInfos; @@ -826,7 +840,7 @@ internal class Vp8Encoder : IDisposable private void SetSegmentProbas() { - int[] p = new int[NumMbSegments]; + Span p = stackalloc int[NumMbSegments]; int n; for (n = 0; n < this.Mbw * this.Mbh; ++n) @@ -917,7 +931,7 @@ internal class Vp8Encoder : IDisposable } } - private int MacroBlockAnalysis(int width, int height, Vp8EncIterator it, Span y, Span u, Span v, int yStride, int uvStride, int[] alphas, out int uvAlpha) + private int MacroBlockAnalysis(int width, int height, Vp8EncIterator it, Span y, Span u, Span v, int yStride, int uvStride, Span alphas, out int uvAlpha) { int alpha = 0; uvAlpha = 0; @@ -938,7 +952,7 @@ internal class Vp8Encoder : IDisposable return alpha; } - private int MbAnalyze(Vp8EncIterator it, int[] alphas, out int bestUvAlpha) + private int MbAnalyze(Vp8EncIterator it, Span alphas, out int bestUvAlpha) { it.SetIntra16Mode(0); // default: Intra16, DC_PRED it.SetSkip(false); // not skipped. @@ -1171,6 +1185,6 @@ internal class Vp8Encoder : IDisposable { int total = a + b; return total == 0 ? 255 // that's the default probability. - : ((255 * a) + (total / 2)) / total; // rounded proba + : ((255 * a) + (total >> 1)) / total; // rounded proba } } diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs index cc263657f1..82f00e8760 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs @@ -521,9 +521,8 @@ internal static unsafe class Vp8Encoding { // *in01 = 00 01 10 11 02 03 12 13 // *in23 = 20 21 30 31 22 23 32 33 - const byte mmShuffle_2301 = 0b_10_11_00_01; - Vector128 shuf01_p = Sse2.ShuffleHigh(row01, mmShuffle_2301); - Vector128 shuf32_p = Sse2.ShuffleHigh(row23, mmShuffle_2301); + Vector128 shuf01_p = Sse2.ShuffleHigh(row01, SimdUtils.Shuffle.MMShuffle2301); + Vector128 shuf32_p = Sse2.ShuffleHigh(row23, SimdUtils.Shuffle.MMShuffle2301); // 00 01 10 11 03 02 13 12 // 20 21 30 31 23 22 33 32 @@ -555,9 +554,7 @@ internal static unsafe class Vp8Encoding Vector128 shi = Sse2.UnpackHigh(s03, s12); // 2 3 2 3 2 3 Vector128 v23 = Sse2.UnpackHigh(slo.AsInt32(), shi.AsInt32()); out01 = Sse2.UnpackLow(slo.AsInt32(), shi.AsInt32()); - - const byte mmShuffle_1032 = 0b_01_00_11_10; - out32 = Sse2.Shuffle(v23, mmShuffle_1032); + out32 = Sse2.Shuffle(v23, SimdUtils.Shuffle.MMShuffle1032); } public static void FTransformPass2SSE2(Vector128 v01, Vector128 v32, Span output) diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Histogram.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Histogram.cs index 4036fb2844..2ace43d2d5 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Histogram.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Histogram.cs @@ -10,12 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy; internal sealed class Vp8Histogram { - private readonly int[] scratch = new int[16]; - - private readonly short[] output = new short[16]; - - private readonly int[] distribution = new int[MaxCoeffThresh + 1]; - /// /// Size of histogram used by CollectHistogram. /// @@ -47,17 +41,20 @@ internal sealed class Vp8Histogram public void CollectHistogram(Span reference, Span pred, int startBlock, int endBlock) { + Span scratch = stackalloc int[16]; + Span output = stackalloc short[16]; + Span distribution = stackalloc int[MaxCoeffThresh + 1]; + int j; - this.distribution.AsSpan().Clear(); for (j = startBlock; j < endBlock; j++) { - Vp8Encoding.FTransform(reference[WebpLookupTables.Vp8DspScan[j]..], pred[WebpLookupTables.Vp8DspScan[j]..], this.output, this.scratch); + Vp8Encoding.FTransform(reference[WebpLookupTables.Vp8DspScan[j]..], pred[WebpLookupTables.Vp8DspScan[j]..], output, scratch); // Convert coefficients to bin. if (Avx2.IsSupported) { // Load. - ref short outputRef = ref MemoryMarshal.GetReference(this.output); + ref short outputRef = ref MemoryMarshal.GetReference(output); Vector256 out0 = Unsafe.As>(ref outputRef); // v = abs(out) >> 3 @@ -73,21 +70,21 @@ internal sealed class Vp8Histogram // Convert coefficients to bin. for (int k = 0; k < 16; ++k) { - ++this.distribution[this.output[k]]; + ++distribution[output[k]]; } } else { for (int k = 0; k < 16; ++k) { - int v = Math.Abs(this.output[k]) >> 3; + int v = Math.Abs(output[k]) >> 3; int clippedValue = ClipMax(v, MaxCoeffThresh); - ++this.distribution[clippedValue]; + ++distribution[clippedValue]; } } } - this.SetHistogramData(this.distribution); + this.SetHistogramData(distribution); } public void Merge(Vp8Histogram other) @@ -103,7 +100,7 @@ internal sealed class Vp8Histogram } } - private void SetHistogramData(int[] distribution) + private void SetHistogramData(ReadOnlySpan distribution) { int maxValue = 0; int lastNonZero = 1; diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs index 7384379dab..1770415062 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs @@ -15,10 +15,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy; /// internal class Vp8Residual { - private readonly byte[] scratch = new byte[32]; - - private readonly ushort[] scratchUShort = new ushort[16]; - public int First { get; set; } public int Last { get; set; } @@ -162,9 +158,10 @@ internal class Vp8Residual if (Avx2.IsSupported) { - Span ctxs = this.scratch.AsSpan(0, 16); - Span levels = this.scratch.AsSpan(16, 16); - Span absLevels = this.scratchUShort.AsSpan(); + Span scratch = stackalloc byte[32]; + Span ctxs = scratch.Slice(0, 16); + Span levels = scratch.Slice(16); + Span absLevels = stackalloc ushort[16]; // Precompute clamped levels and contexts, packed to 8b. ref short outputRef = ref MemoryMarshal.GetReference(this.Coeffs); diff --git a/src/ImageSharp/Formats/Webp/Lossy/WebpLossyDecoder.cs b/src/ImageSharp/Formats/Webp/Lossy/WebpLossyDecoder.cs index e63a7ef74e..7952b15b44 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/WebpLossyDecoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/WebpLossyDecoder.cs @@ -34,16 +34,6 @@ internal sealed class WebpLossyDecoder /// private readonly Configuration configuration; - /// - /// Scratch buffer to reduce allocations. - /// - private readonly int[] scratch = new int[16]; - - /// - /// Another scratch buffer to reduce allocations. - /// - private readonly byte[] scratchBytes = new byte[4]; - /// /// Initializes a new instance of the class. /// @@ -371,6 +361,9 @@ internal sealed class WebpLossyDecoder } } + Span scratch = stackalloc int[16]; + Span scratchBytes = stackalloc byte[4]; + // Reconstruct one row. for (int mbx = 0; mbx < dec.MbWidth; mbx++) { @@ -448,7 +441,7 @@ internal sealed class WebpLossyDecoder LossyUtils.TM4(dst, yuv, offset); break; case 2: - LossyUtils.VE4(dst, yuv, offset, this.scratchBytes); + LossyUtils.VE4(dst, yuv, offset, scratchBytes); break; case 3: LossyUtils.HE4(dst, yuv, offset); @@ -473,7 +466,7 @@ internal sealed class WebpLossyDecoder break; } - DoTransform(bits, coeffs.AsSpan(n * 16), dst, this.scratch); + DoTransform(bits, coeffs.AsSpan(n * 16), dst, scratch); } } else @@ -508,7 +501,7 @@ internal sealed class WebpLossyDecoder { for (int n = 0; n < 16; ++n, bits <<= 2) { - DoTransform(bits, coeffs.AsSpan(n * 16), yDst[WebpConstants.Scan[n]..], this.scratch); + DoTransform(bits, coeffs.AsSpan(n * 16), yDst[WebpConstants.Scan[n]..], scratch); } } } @@ -547,8 +540,8 @@ internal sealed class WebpLossyDecoder break; } - DoUVTransform(bitsUv, coeffs.AsSpan(16 * 16), uDst, this.scratch); - DoUVTransform(bitsUv >> 8, coeffs.AsSpan(20 * 16), vDst, this.scratch); + DoUVTransform(bitsUv, coeffs.AsSpan(16 * 16), uDst, scratch); + DoUVTransform(bitsUv >> 8, coeffs.AsSpan(20 * 16), vDst, scratch); // Stash away top samples for next block. if (mby < dec.MbHeight - 1) @@ -731,7 +724,7 @@ internal sealed class WebpLossyDecoder Span dst = buf[dstStartIdx..]; int yEnd = io.MbY + io.MbH; int mbw = io.MbW; - int uvw = (mbw + 1) / 2; + int uvw = (mbw + 1) >> 1; // >> 1 is bit-hack for / 2 int y = io.MbY; byte[] uvBuffer = new byte[(14 * 32) + 15]; @@ -875,14 +868,14 @@ internal sealed class WebpLossyDecoder else { // Parse DC - short[] dc = new short[16]; + Span dc = stackalloc short[16]; int ctx = (int)(mb.NoneZeroDcCoeffs + leftMb.NoneZeroDcCoeffs); int nz = GetCoeffs(br, bands[1], ctx, q.Y2Mat, 0, dc); mb.NoneZeroDcCoeffs = leftMb.NoneZeroDcCoeffs = (uint)(nz > 0 ? 1 : 0); if (nz > 1) { // More than just the DC -> perform the full transform. - LossyUtils.TransformWht(dc, dst, this.scratch); + LossyUtils.TransformWht(dc, dst, stackalloc int[16]); } else { diff --git a/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs b/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs index 5c6dde6224..8ef7fe9cba 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs @@ -138,8 +138,8 @@ internal static class YuvConversion { for (pos = 1, uvPos = 0; pos + 32 + 1 <= len; pos += 32, uvPos += 16) { - UpSample32Pixels(ref Unsafe.Add(ref topURef, uvPos), ref Unsafe.Add(ref curURef, uvPos), ru); - UpSample32Pixels(ref Unsafe.Add(ref topVRef, uvPos), ref Unsafe.Add(ref curVRef, uvPos), rv); + UpSample32Pixels(ref Unsafe.Add(ref topURef, (uint)uvPos), ref Unsafe.Add(ref curURef, (uint)uvPos), ru); + UpSample32Pixels(ref Unsafe.Add(ref topVRef, (uint)uvPos), ref Unsafe.Add(ref curVRef, (uint)uvPos), rv); ConvertYuvToBgrWithBottomYSse41(topY, bottomY, topDst, bottomDst, ru, rv, pos, xStep); } } @@ -147,8 +147,8 @@ internal static class YuvConversion { for (pos = 1, uvPos = 0; pos + 32 + 1 <= len; pos += 32, uvPos += 16) { - UpSample32Pixels(ref Unsafe.Add(ref topURef, uvPos), ref Unsafe.Add(ref curURef, uvPos), ru); - UpSample32Pixels(ref Unsafe.Add(ref topVRef, uvPos), ref Unsafe.Add(ref curVRef, uvPos), rv); + UpSample32Pixels(ref Unsafe.Add(ref topURef, (uint)uvPos), ref Unsafe.Add(ref curURef, (uint)uvPos), ru); + UpSample32Pixels(ref Unsafe.Add(ref topVRef, (uint)uvPos), ref Unsafe.Add(ref curVRef, (uint)uvPos), rv); ConvertYuvToBgrSse41(topY, topDst, ru, rv, pos, xStep); } } diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs index 4c1c60591d..21337ce6c8 100644 --- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Runtime.CompilerServices; @@ -18,11 +17,6 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// internal class WebpAnimationDecoder : IDisposable { - /// - /// Reusable buffer. - /// - private readonly byte[] buffer = new byte[4]; - /// /// Used for allocating memory during the decoding operations. /// @@ -46,17 +40,17 @@ internal class WebpAnimationDecoder : IDisposable /// /// The abstract metadata. /// - private ImageMetadata metadata; + private ImageMetadata? metadata; /// /// The gif specific metadata. /// - private WebpMetadata webpMetadata; + private WebpMetadata? webpMetadata; /// /// The alpha data, if an ALPH chunk is present. /// - private IMemoryOwner alphaData; + private IMemoryOwner? alphaData; /// /// Initializes a new instance of the class. @@ -83,28 +77,29 @@ internal class WebpAnimationDecoder : IDisposable public Image Decode(BufferedReadStream stream, WebpFeatures features, uint width, uint height, uint completeDataSize) where TPixel : unmanaged, IPixel { - Image image = null; - ImageFrame previousFrame = null; + Image? image = null; + ImageFrame? previousFrame = null; this.metadata = new ImageMetadata(); this.webpMetadata = this.metadata.GetWebpMetadata(); this.webpMetadata.AnimationLoopCount = features.AnimationLoopCount; + Span buffer = stackalloc byte[4]; uint frameCount = 0; int remainingBytes = (int)completeDataSize; while (remainingBytes > 0) { - WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, this.buffer); + WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer); remainingBytes -= 4; switch (chunkType) { case WebpChunkType.Animation: - uint dataSize = this.ReadFrame(stream, ref image, ref previousFrame, width, height, features.AnimationBackgroundColor.Value); + uint dataSize = this.ReadFrame(stream, ref image, ref previousFrame, width, height, features.AnimationBackgroundColor!.Value); remainingBytes -= (int)dataSize; break; case WebpChunkType.Xmp: case WebpChunkType.Exif: - WebpChunkParsingUtils.ParseOptionalChunks(stream, chunkType, image.Metadata, false, this.buffer); + WebpChunkParsingUtils.ParseOptionalChunks(stream, chunkType, image!.Metadata, false, buffer); break; default: WebpThrowHelper.ThrowImageFormatException("Read unexpected webp chunk data"); @@ -117,7 +112,7 @@ internal class WebpAnimationDecoder : IDisposable } } - return image; + return image!; } /// @@ -130,40 +125,41 @@ internal class WebpAnimationDecoder : IDisposable /// The width of the image. /// The height of the image. /// The default background color of the canvas in. - private uint ReadFrame(BufferedReadStream stream, ref Image image, ref ImageFrame previousFrame, uint width, uint height, Color backgroundColor) + private uint ReadFrame(BufferedReadStream stream, ref Image? image, ref ImageFrame? previousFrame, uint width, uint height, Color backgroundColor) where TPixel : unmanaged, IPixel { AnimationFrameData frameData = this.ReadFrameHeader(stream); long streamStartPosition = stream.Position; + Span buffer = stackalloc byte[4]; - WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, this.buffer); + WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer); bool hasAlpha = false; byte alphaChunkHeader = 0; if (chunkType is WebpChunkType.Alpha) { alphaChunkHeader = this.ReadAlphaData(stream); hasAlpha = true; - chunkType = WebpChunkParsingUtils.ReadChunkType(stream, this.buffer); + chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer); } - WebpImageInfo webpInfo = null; + WebpImageInfo? webpInfo = null; WebpFeatures features = new(); switch (chunkType) { case WebpChunkType.Vp8: - webpInfo = WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, this.buffer, features); + webpInfo = WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, buffer, features); features.Alpha = hasAlpha; features.AlphaChunkHeader = alphaChunkHeader; break; case WebpChunkType.Vp8L: - webpInfo = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, this.buffer, features); + webpInfo = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features); break; default: WebpThrowHelper.ThrowImageFormatException("Read unexpected chunk type, should be VP8 or VP8L"); break; } - ImageFrame currentFrame = null; + ImageFrame? currentFrame = null; ImageFrame imageFrame; if (previousFrame is null) { @@ -175,7 +171,7 @@ internal class WebpAnimationDecoder : IDisposable } else { - currentFrame = image.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection. + currentFrame = image!.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection. SetFrameMetadata(currentFrame.Metadata, frameData.Duration); @@ -227,7 +223,7 @@ internal class WebpAnimationDecoder : IDisposable { this.alphaData?.Dispose(); - uint alphaChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, this.buffer); + uint alphaChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, stackalloc byte[4]); int alphaDataSize = (int)(alphaChunkSize - 1); this.alphaData = this.memoryAllocator.Allocate(alphaDataSize); @@ -354,24 +350,26 @@ internal class WebpAnimationDecoder : IDisposable /// Animation frame data. private AnimationFrameData ReadFrameHeader(BufferedReadStream stream) { + Span buffer = stackalloc byte[4]; + AnimationFrameData data = new() { - DataSize = WebpChunkParsingUtils.ReadChunkSize(stream, this.buffer), + DataSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer), // 3 bytes for the X coordinate of the upper left corner of the frame. - X = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer), + X = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, buffer), // 3 bytes for the Y coordinate of the upper left corner of the frame. - Y = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer), + Y = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, buffer), // Frame width Minus One. - Width = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer) + 1, + Width = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, buffer) + 1, // Frame height Minus One. - Height = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer) + 1, + Height = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, buffer) + 1, // Frame duration. - Duration = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer) + Duration = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, buffer) }; byte flags = (byte)stream.ReadByte(); diff --git a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs index bfe415a6e4..a7ae474e46 100644 --- a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs +++ b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers.Binary; using SixLabors.ImageSharp.Formats.Webp.BitReader; @@ -19,7 +18,7 @@ internal static class WebpChunkParsingUtils /// Reads the header of a lossy webp image. /// /// Information about this webp image. - public static WebpImageInfo ReadVp8Header(MemoryAllocator memoryAllocator, BufferedReadStream stream, byte[] buffer, WebpFeatures features) + public static WebpImageInfo ReadVp8Header(MemoryAllocator memoryAllocator, BufferedReadStream stream, Span buffer, WebpFeatures features) { // VP8 data size (not including this 4 bytes). int bytesRead = stream.Read(buffer, 0, 4); @@ -78,7 +77,7 @@ internal static class WebpChunkParsingUtils WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 magic bytes"); } - if (!buffer.AsSpan(0, 3).SequenceEqual(WebpConstants.Vp8HeaderMagicBytes)) + if (!buffer.Slice(0, 3).SequenceEqual(WebpConstants.Vp8HeaderMagicBytes)) { WebpThrowHelper.ThrowImageFormatException("VP8 magic bytes not found"); } @@ -92,7 +91,7 @@ internal static class WebpChunkParsingUtils uint tmp = BinaryPrimitives.ReadUInt16LittleEndian(buffer); uint width = tmp & 0x3fff; sbyte xScale = (sbyte)(tmp >> 6); - tmp = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(2)); + tmp = BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(2)); uint height = tmp & 0x3fff; sbyte yScale = (sbyte)(tmp >> 6); remaining -= 7; @@ -141,7 +140,7 @@ internal static class WebpChunkParsingUtils /// Reads the header of a lossless webp image. /// /// Information about this image. - public static WebpImageInfo ReadVp8LHeader(MemoryAllocator memoryAllocator, BufferedReadStream stream, byte[] buffer, WebpFeatures features) + public static WebpImageInfo ReadVp8LHeader(MemoryAllocator memoryAllocator, BufferedReadStream stream, Span buffer, WebpFeatures features) { // VP8 data size. uint imageDataSize = ReadChunkSize(stream, buffer); @@ -196,7 +195,7 @@ internal static class WebpChunkParsingUtils /// After the image header, image data will follow. After that optional image metadata chunks (EXIF and XMP) can follow. /// /// Information about this webp image. - public static WebpImageInfo ReadVp8XHeader(BufferedReadStream stream, byte[] buffer, WebpFeatures features) + public static WebpImageInfo ReadVp8XHeader(BufferedReadStream stream, Span buffer, WebpFeatures features) { uint fileSize = ReadChunkSize(stream, buffer); @@ -254,7 +253,7 @@ internal static class WebpChunkParsingUtils /// The stream to read from. /// The buffer to store the read data into. /// A unsigned 24 bit integer. - public static uint ReadUnsignedInt24Bit(BufferedReadStream stream, byte[] buffer) + public static uint ReadUnsignedInt24Bit(BufferedReadStream stream, Span buffer) { if (stream.Read(buffer, 0, 3) == 3) { @@ -272,9 +271,11 @@ internal static class WebpChunkParsingUtils /// The stream to read the data from. /// Buffer to store the data read from the stream. /// The chunk size in bytes. - public static uint ReadChunkSize(BufferedReadStream stream, byte[] buffer) + public static uint ReadChunkSize(BufferedReadStream stream, Span buffer) { - if (stream.Read(buffer, 0, 4) == 4) + DebugGuard.IsTrue(buffer.Length == 4, "buffer has wrong length"); + + if (stream.Read(buffer) == 4) { uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(buffer); return (chunkSize % 2 == 0) ? chunkSize : chunkSize + 1; @@ -291,9 +292,11 @@ internal static class WebpChunkParsingUtils /// /// Thrown if the input stream is not valid. /// - public static WebpChunkType ReadChunkType(BufferedReadStream stream, byte[] buffer) + public static WebpChunkType ReadChunkType(BufferedReadStream stream, Span buffer) { - if (stream.Read(buffer, 0, 4) == 4) + DebugGuard.IsTrue(buffer.Length == 4, "buffer has wrong length"); + + if (stream.Read(buffer) == 4) { var chunkType = (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer); return chunkType; @@ -307,7 +310,7 @@ internal static class WebpChunkParsingUtils /// If there are more such chunks, readers MAY ignore all except the first one. /// Also, a file may possibly contain both 'EXIF' and 'XMP ' chunks. /// - public static void ParseOptionalChunks(BufferedReadStream stream, WebpChunkType chunkType, ImageMetadata metadata, bool ignoreMetaData, byte[] buffer) + public static void ParseOptionalChunks(BufferedReadStream stream, WebpChunkType chunkType, ImageMetadata metadata, bool ignoreMetaData, Span buffer) { long streamLength = stream.Length; while (stream.Position < streamLength) diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 29be86e22f..223e15a0e7 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Buffers.Binary; @@ -9,9 +8,7 @@ using SixLabors.ImageSharp.Formats.Webp.Lossy; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; -using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; -using SixLabors.ImageSharp.Metadata.Profiles.Xmp; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Webp; @@ -21,11 +18,6 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable { - /// - /// Reusable buffer. - /// - private readonly byte[] buffer = new byte[4]; - /// /// General configuration options. /// @@ -41,35 +33,20 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable /// private readonly uint maxFrames; - /// - /// Gets the decoded by this decoder instance. - /// - private ImageMetadata metadata; - /// /// Gets or sets the alpha data, if an ALPH chunk is present. /// - private IMemoryOwner alphaData; + private IMemoryOwner? alphaData; /// /// Used for allocating memory during the decoding operations. /// private readonly MemoryAllocator memoryAllocator; - /// - /// The stream to decode from. - /// - private BufferedReadStream currentStream; - - /// - /// The webp specific metadata. - /// - private WebpMetadata webpMetadata; - /// /// Information about the webp image. /// - private WebpImageInfo webImageInfo; + private WebpImageInfo? webImageInfo; /// /// Initializes a new instance of the class. @@ -88,25 +65,25 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable public DecoderOptions Options { get; } /// - public Size Dimensions => new((int)this.webImageInfo.Width, (int)this.webImageInfo.Height); + public Size Dimensions => new((int)this.webImageInfo!.Width, (int)this.webImageInfo.Height); /// public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - Image image = null; + Image? image = null; try { - this.metadata = new ImageMetadata(); - this.currentStream = stream; + ImageMetadata metadata = new(); + Span buffer = stackalloc byte[4]; - uint fileSize = this.ReadImageHeader(); + uint fileSize = ReadImageHeader(stream, buffer); - using (this.webImageInfo = this.ReadVp8Info()) + using (this.webImageInfo = this.ReadVp8Info(stream, metadata)) { if (this.webImageInfo.Features is { Animation: true }) { - using var animationDecoder = new WebpAnimationDecoder(this.memoryAllocator, this.configuration, this.maxFrames); + using WebpAnimationDecoder animationDecoder = new(this.memoryAllocator, this.configuration, this.maxFrames); return animationDecoder.Decode(stream, this.webImageInfo.Features, this.webImageInfo.Width, this.webImageInfo.Height, fileSize); } @@ -115,23 +92,23 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable WebpThrowHelper.ThrowNotSupportedException("Animations are not supported"); } - image = new Image(this.configuration, (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, this.metadata); + image = new Image(this.configuration, (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, metadata); Buffer2D pixels = image.GetRootFramePixelBuffer(); if (this.webImageInfo.IsLossless) { - var losslessDecoder = new WebpLosslessDecoder(this.webImageInfo.Vp8LBitReader, this.memoryAllocator, this.configuration); + WebpLosslessDecoder losslessDecoder = new(this.webImageInfo.Vp8LBitReader, this.memoryAllocator, this.configuration); losslessDecoder.Decode(pixels, image.Width, image.Height); } else { - var lossyDecoder = new WebpLossyDecoder(this.webImageInfo.Vp8BitReader, this.memoryAllocator, this.configuration); + WebpLossyDecoder lossyDecoder = new(this.webImageInfo.Vp8BitReader, this.memoryAllocator, this.configuration); lossyDecoder.Decode(pixels, image.Width, image.Height, this.webImageInfo, this.alphaData); } // There can be optional chunks after the image data, like EXIF and XMP. if (this.webImageInfo.Features != null) { - this.ParseOptionalChunks(this.webImageInfo.Features); + this.ParseOptionalChunks(stream, metadata, this.webImageInfo.Features, buffer); } return image; @@ -147,31 +124,36 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable /// public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { - this.currentStream = stream; + ReadImageHeader(stream, stackalloc byte[4]); - this.ReadImageHeader(); - using (this.webImageInfo = this.ReadVp8Info(true)) + ImageMetadata metadata = new(); + using (this.webImageInfo = this.ReadVp8Info(stream, metadata, true)) { - return new ImageInfo(new PixelTypeInfo((int)this.webImageInfo.BitsPerPixel), (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, this.metadata); + return new ImageInfo( + new PixelTypeInfo((int)this.webImageInfo.BitsPerPixel), + new((int)this.webImageInfo.Width, (int)this.webImageInfo.Height), + metadata); } } /// /// Reads and skips over the image header. /// + /// The stream to decode from. + /// Temporary buffer. /// The file size in bytes. - private uint ReadImageHeader() + private static uint ReadImageHeader(BufferedReadStream stream, Span buffer) { // Skip FourCC header, we already know its a RIFF file at this point. - this.currentStream.Skip(4); + stream.Skip(4); // Read file size. // The size of the file in bytes starting at offset 8. // The file size in the header is the total size of the chunks that follow plus 4 bytes for the ‘WEBP’ FourCC. - uint fileSize = WebpChunkParsingUtils.ReadChunkSize(this.currentStream, this.buffer); + uint fileSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer); // Skip 'WEBP' from the header. - this.currentStream.Skip(4); + stream.Skip(4); return fileSize; } @@ -179,42 +161,44 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable /// /// Reads information present in the image header, about the image content and how to decode the image. /// + /// The stream to decode from. + /// The image metadata. /// For identify, the alpha data should not be read. /// Information about the webp image. - private WebpImageInfo ReadVp8Info(bool ignoreAlpha = false) + private WebpImageInfo ReadVp8Info(BufferedReadStream stream, ImageMetadata metadata, bool ignoreAlpha = false) { - this.metadata = new ImageMetadata(); - this.webpMetadata = this.metadata.GetFormatMetadata(WebpFormat.Instance); + WebpMetadata webpMetadata = metadata.GetFormatMetadata(WebpFormat.Instance); - WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(this.currentStream, this.buffer); + Span buffer = stackalloc byte[4]; + WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer); - var features = new WebpFeatures(); + WebpFeatures features = new(); switch (chunkType) { case WebpChunkType.Vp8: - this.webpMetadata.FileFormat = WebpFileFormatType.Lossy; - return WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, this.currentStream, this.buffer, features); + webpMetadata.FileFormat = WebpFileFormatType.Lossy; + return WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, buffer, features); case WebpChunkType.Vp8L: - this.webpMetadata.FileFormat = WebpFileFormatType.Lossless; - return WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, this.currentStream, this.buffer, features); + webpMetadata.FileFormat = WebpFileFormatType.Lossless; + return WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features); case WebpChunkType.Vp8X: - WebpImageInfo webpInfos = WebpChunkParsingUtils.ReadVp8XHeader(this.currentStream, this.buffer, features); - while (this.currentStream.Position < this.currentStream.Length) + WebpImageInfo webpInfos = WebpChunkParsingUtils.ReadVp8XHeader(stream, buffer, features); + while (stream.Position < stream.Length) { - chunkType = WebpChunkParsingUtils.ReadChunkType(this.currentStream, this.buffer); + chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer); if (chunkType == WebpChunkType.Vp8) { - this.webpMetadata.FileFormat = WebpFileFormatType.Lossy; - webpInfos = WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, this.currentStream, this.buffer, features); + webpMetadata.FileFormat = WebpFileFormatType.Lossy; + webpInfos = WebpChunkParsingUtils.ReadVp8Header(this.memoryAllocator, stream, buffer, features); } else if (chunkType == WebpChunkType.Vp8L) { - this.webpMetadata.FileFormat = WebpFileFormatType.Lossless; - webpInfos = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, this.currentStream, this.buffer, features); + webpMetadata.FileFormat = WebpFileFormatType.Lossless; + webpInfos = WebpChunkParsingUtils.ReadVp8LHeader(this.memoryAllocator, stream, buffer, features); } else if (WebpChunkParsingUtils.IsOptionalVp8XChunk(chunkType)) { - bool isAnimationChunk = this.ParseOptionalExtendedChunks(chunkType, features, ignoreAlpha); + bool isAnimationChunk = this.ParseOptionalExtendedChunks(stream, metadata, chunkType, features, ignoreAlpha, buffer); if (isAnimationChunk) { return webpInfos; @@ -223,8 +207,8 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable else { // Ignore unknown chunks. - uint chunkSize = this.ReadChunkSize(); - this.currentStream.Skip((int)chunkSize); + uint chunkSize = ReadChunkSize(stream, buffer); + stream.Skip((int)chunkSize); } } @@ -239,32 +223,41 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable /// /// Parses optional VP8X chunks, which can be ICCP, XMP, ANIM or ALPH chunks. /// + /// The stream to decode from. + /// The image metadata. /// The chunk type. /// The webp image features. /// For identify, the alpha data should not be read. + /// Temporary buffer. /// true, if its a alpha chunk. - private bool ParseOptionalExtendedChunks(WebpChunkType chunkType, WebpFeatures features, bool ignoreAlpha) + private bool ParseOptionalExtendedChunks( + BufferedReadStream stream, + ImageMetadata metadata, + WebpChunkType chunkType, + WebpFeatures features, + bool ignoreAlpha, + Span buffer) { switch (chunkType) { case WebpChunkType.Iccp: - this.ReadIccProfile(); + this.ReadIccProfile(stream, metadata, buffer); break; case WebpChunkType.Exif: - this.ReadExifProfile(); + this.ReadExifProfile(stream, metadata, buffer); break; case WebpChunkType.Xmp: - this.ReadXmpProfile(); + this.ReadXmpProfile(stream, metadata, buffer); break; case WebpChunkType.AnimationParameter: - this.ReadAnimationParameters(features); + ReadAnimationParameters(stream, features, buffer); return true; case WebpChunkType.Alpha: - this.ReadAlphaData(features, ignoreAlpha); + this.ReadAlphaData(stream, features, ignoreAlpha, buffer); break; default: WebpThrowHelper.ThrowImageFormatException("Unexpected chunk followed VP8X header"); @@ -277,32 +270,35 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable /// /// Reads the optional metadata EXIF of XMP profiles, which can follow the image data. /// + /// The stream to decode from. + /// The image metadata. /// The webp features. - private void ParseOptionalChunks(WebpFeatures features) + /// Temporary buffer. + private void ParseOptionalChunks(BufferedReadStream stream, ImageMetadata metadata, WebpFeatures features, Span buffer) { if (this.skipMetadata || (!features.ExifProfile && !features.XmpMetaData)) { return; } - long streamLength = this.currentStream.Length; - while (this.currentStream.Position < streamLength) + long streamLength = stream.Length; + while (stream.Position < streamLength) { // Read chunk header. - WebpChunkType chunkType = this.ReadChunkType(); - if (chunkType == WebpChunkType.Exif && this.metadata.ExifProfile == null) + WebpChunkType chunkType = ReadChunkType(stream, buffer); + if (chunkType == WebpChunkType.Exif && metadata.ExifProfile == null) { - this.ReadExifProfile(); + this.ReadExifProfile(stream, metadata, buffer); } - else if (chunkType == WebpChunkType.Xmp && this.metadata.XmpProfile == null) + else if (chunkType == WebpChunkType.Xmp && metadata.XmpProfile == null) { - this.ReadXmpProfile(); + this.ReadXmpProfile(stream, metadata, buffer); } else { // Skip duplicate XMP or EXIF chunk. - uint chunkLength = this.ReadChunkSize(); - this.currentStream.Skip((int)chunkLength); + uint chunkLength = ReadChunkSize(stream, buffer); + stream.Skip((int)chunkLength); } } } @@ -310,76 +306,83 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable /// /// Reads the EXIF profile from the stream. /// - private void ReadExifProfile() + /// The stream to decode from. + /// The image metadata. + /// Temporary buffer. + private void ReadExifProfile(BufferedReadStream stream, ImageMetadata metadata, Span buffer) { - uint exifChunkSize = this.ReadChunkSize(); + uint exifChunkSize = ReadChunkSize(stream, buffer); if (this.skipMetadata) { - this.currentStream.Skip((int)exifChunkSize); + stream.Skip((int)exifChunkSize); } else { byte[] exifData = new byte[exifChunkSize]; - int bytesRead = this.currentStream.Read(exifData, 0, (int)exifChunkSize); + int bytesRead = stream.Read(exifData, 0, (int)exifChunkSize); if (bytesRead != exifChunkSize) { // Ignore invalid chunk. return; } - var profile = new ExifProfile(exifData); - this.metadata.ExifProfile = profile; + metadata.ExifProfile = new(exifData); } } /// /// Reads the XMP profile the stream. /// - private void ReadXmpProfile() + /// The stream to decode from. + /// The image metadata. + /// Temporary buffer. + private void ReadXmpProfile(BufferedReadStream stream, ImageMetadata metadata, Span buffer) { - uint xmpChunkSize = this.ReadChunkSize(); + uint xmpChunkSize = ReadChunkSize(stream, buffer); if (this.skipMetadata) { - this.currentStream.Skip((int)xmpChunkSize); + stream.Skip((int)xmpChunkSize); } else { byte[] xmpData = new byte[xmpChunkSize]; - int bytesRead = this.currentStream.Read(xmpData, 0, (int)xmpChunkSize); + int bytesRead = stream.Read(xmpData, 0, (int)xmpChunkSize); if (bytesRead != xmpChunkSize) { // Ignore invalid chunk. return; } - var profile = new XmpProfile(xmpData); - this.metadata.XmpProfile = profile; + metadata.XmpProfile = new(xmpData); } } /// /// Reads the ICCP chunk from the stream. /// - private void ReadIccProfile() + /// The stream to decode from. + /// The image metadata. + /// Temporary buffer. + private void ReadIccProfile(BufferedReadStream stream, ImageMetadata metadata, Span buffer) { - uint iccpChunkSize = this.ReadChunkSize(); + uint iccpChunkSize = ReadChunkSize(stream, buffer); if (this.skipMetadata) { - this.currentStream.Skip((int)iccpChunkSize); + stream.Skip((int)iccpChunkSize); } else { byte[] iccpData = new byte[iccpChunkSize]; - int bytesRead = this.currentStream.Read(iccpData, 0, (int)iccpChunkSize); + int bytesRead = stream.Read(iccpData, 0, (int)iccpChunkSize); if (bytesRead != iccpChunkSize) { WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the iccp chunk"); } - var profile = new IccProfile(iccpData); + IccProfile profile = new(iccpData); if (profile.CheckIsValid()) { - this.metadata.IccProfile = profile; + metadata.IccProfile = profile; } } } @@ -387,44 +390,48 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable /// /// Reads the animation parameters chunk from the stream. /// + /// The stream to decode from. /// The webp features. - private void ReadAnimationParameters(WebpFeatures features) + /// Temporary buffer. + private static void ReadAnimationParameters(BufferedReadStream stream, WebpFeatures features, Span buffer) { features.Animation = true; - uint animationChunkSize = WebpChunkParsingUtils.ReadChunkSize(this.currentStream, this.buffer); - byte blue = (byte)this.currentStream.ReadByte(); - byte green = (byte)this.currentStream.ReadByte(); - byte red = (byte)this.currentStream.ReadByte(); - byte alpha = (byte)this.currentStream.ReadByte(); + uint animationChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer); + byte blue = (byte)stream.ReadByte(); + byte green = (byte)stream.ReadByte(); + byte red = (byte)stream.ReadByte(); + byte alpha = (byte)stream.ReadByte(); features.AnimationBackgroundColor = new Color(new Rgba32(red, green, blue, alpha)); - int bytesRead = this.currentStream.Read(this.buffer, 0, 2); + int bytesRead = stream.Read(buffer, 0, 2); if (bytesRead != 2) { WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the animation loop count"); } - features.AnimationLoopCount = BinaryPrimitives.ReadUInt16LittleEndian(this.buffer); + features.AnimationLoopCount = BinaryPrimitives.ReadUInt16LittleEndian(buffer); } /// /// Reads the alpha data chunk data from the stream. /// + /// The stream to decode from. /// The features. /// if set to true, skips the chunk data. - private void ReadAlphaData(WebpFeatures features, bool ignoreAlpha) + /// Temporary buffer. + private void ReadAlphaData(BufferedReadStream stream, WebpFeatures features, bool ignoreAlpha, Span buffer) { - uint alphaChunkSize = WebpChunkParsingUtils.ReadChunkSize(this.currentStream, this.buffer); + uint alphaChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer); if (ignoreAlpha) { - this.currentStream.Skip((int)alphaChunkSize); + stream.Skip((int)alphaChunkSize); return; } - features.AlphaChunkHeader = (byte)this.currentStream.ReadByte(); + features.AlphaChunkHeader = (byte)stream.ReadByte(); int alphaDataSize = (int)(alphaChunkSize - 1); this.alphaData = this.memoryAllocator.Allocate(alphaDataSize); Span alphaData = this.alphaData.GetSpan(); - int bytesRead = this.currentStream.Read(alphaData, 0, alphaDataSize); + int bytesRead = stream.Read(alphaData, 0, alphaDataSize); if (bytesRead != alphaDataSize) { WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the alpha data from the stream"); @@ -434,15 +441,16 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable /// /// Identifies the chunk type from the chunk. /// + /// The stream to decode from. + /// Temporary buffer. /// /// Thrown if the input stream is not valid. /// - private WebpChunkType ReadChunkType() + private static WebpChunkType ReadChunkType(BufferedReadStream stream, Span buffer) { - if (this.currentStream.Read(this.buffer, 0, 4) == 4) + if (stream.Read(buffer, 0, 4) == 4) { - var chunkType = (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer); - return chunkType; + return (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer); } throw new ImageFormatException("Invalid Webp data."); @@ -452,12 +460,15 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable /// Reads the chunk size. If Chunk Size is odd, a single padding byte will be added to the payload, /// so the chunk size will be increased by 1 in those cases. /// + /// The stream to decode from. + /// Temporary buffer. /// The chunk size in bytes. - private uint ReadChunkSize() + /// Invalid data. + private static uint ReadChunkSize(BufferedReadStream stream, Span buffer) { - if (this.currentStream.Read(this.buffer, 0, 4) == 4) + if (stream.Read(buffer, 0, 4) == 4) { - uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); + uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(buffer); return (chunkSize % 2 == 0) ? chunkSize : chunkSize + 1; } diff --git a/src/ImageSharp/Formats/Webp/WebpEncoder.cs b/src/ImageSharp/Formats/Webp/WebpEncoder.cs index e314d38017..bd8303f1c8 100644 --- a/src/ImageSharp/Formats/Webp/WebpEncoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpEncoder.cs @@ -82,7 +82,7 @@ public sealed class WebpEncoder : ImageEncoder /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { - WebpEncoderCore encoder = new(this, image.GetMemoryAllocator()); + WebpEncoderCore encoder = new(this, image.GetConfiguration()); encoder.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs index 68951e5ec0..49512e03b5 100644 --- a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Formats.Webp.Lossy; using SixLabors.ImageSharp.Memory; @@ -29,7 +27,7 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals /// /// Compression quality. Between 0 and 100. /// - private readonly int quality; + private readonly uint quality; /// /// Quality/speed trade-off (0=fast, 6=slower-better). @@ -81,19 +79,20 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals /// /// The global configuration. /// - private Configuration configuration; + private readonly Configuration configuration; /// /// Initializes a new instance of the class. /// /// The encoder with options. - /// The memory manager. - public WebpEncoderCore(WebpEncoder encoder, MemoryAllocator memoryAllocator) + /// The global configuration. + public WebpEncoderCore(WebpEncoder encoder, Configuration configuration) { - this.memoryAllocator = memoryAllocator; + this.configuration = configuration; + this.memoryAllocator = configuration.MemoryAllocator; this.alphaCompression = encoder.UseAlphaCompression; this.fileFormat = encoder.FileFormat; - this.quality = encoder.Quality; + this.quality = (uint)encoder.Quality; this.method = encoder.Method; this.entropyPasses = encoder.EntropyPasses; this.spatialNoiseShaping = encoder.SpatialNoiseShaping; @@ -117,7 +116,6 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - this.configuration = image.GetConfiguration(); bool lossless; if (this.fileFormat is not null) { diff --git a/src/ImageSharp/Formats/Webp/WebpImageInfo.cs b/src/ImageSharp/Formats/Webp/WebpImageInfo.cs index c41403211f..5f7301b262 100644 --- a/src/ImageSharp/Formats/Webp/WebpImageInfo.cs +++ b/src/ImageSharp/Formats/Webp/WebpImageInfo.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using SixLabors.ImageSharp.Formats.Webp.BitReader; using SixLabors.ImageSharp.Formats.Webp.Lossy; @@ -36,7 +35,7 @@ internal class WebpImageInfo : IDisposable /// /// Gets or sets additional features present in a VP8X image. /// - public WebpFeatures Features { get; set; } + public WebpFeatures? Features { get; set; } /// /// Gets or sets the VP8 profile / version. Valid values are between 0 and 3. Default value will be the invalid value -1. @@ -46,17 +45,17 @@ internal class WebpImageInfo : IDisposable /// /// Gets or sets the VP8 frame header. /// - public Vp8FrameHeader Vp8FrameHeader { get; set; } + public Vp8FrameHeader? Vp8FrameHeader { get; set; } /// /// Gets or sets the VP8L bitreader. Will be , if its not a lossless image. /// - public Vp8LBitReader Vp8LBitReader { get; set; } + public Vp8LBitReader? Vp8LBitReader { get; set; } /// /// Gets or sets the VP8 bitreader. Will be , if its not a lossy image. /// - public Vp8BitReader Vp8BitReader { get; set; } + public Vp8BitReader? Vp8BitReader { get; set; } /// public void Dispose() diff --git a/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs b/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs index 944cefa745..c633c52738 100644 --- a/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs +++ b/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs @@ -1,15 +1,21 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; + namespace SixLabors.ImageSharp.Formats.Webp; internal static class WebpThrowHelper { + [DoesNotReturn] public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage); + [DoesNotReturn] public static void ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage); + [DoesNotReturn] public static void ThrowNotSupportedException(string errorMessage) => throw new NotSupportedException(errorMessage); + [DoesNotReturn] public static void ThrowInvalidImageDimensions(string errorMessage) => throw new InvalidImageContentException(errorMessage); } diff --git a/src/ImageSharp/IO/ChunkedMemoryStream.cs b/src/ImageSharp/IO/ChunkedMemoryStream.cs index da52f7ca85..2534548141 100644 --- a/src/ImageSharp/IO/ChunkedMemoryStream.cs +++ b/src/ImageSharp/IO/ChunkedMemoryStream.cs @@ -547,7 +547,7 @@ internal sealed class ChunkedMemoryStream : Stream #pragma warning disable IDE1006 // Naming Styles const int _128K = 1 << 17; const int _4M = 1 << 22; - return i < 16 ? _128K * (1 << (i / 4)) : _4M; + return i < 16 ? _128K * (1 << (int)((uint)i / 4)) : _4M; #pragma warning restore IDE1006 // Naming Styles } diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 02fa014781..cba32cb782 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp; /// For the non-generic type, the pixel type is only known at runtime. /// is always implemented by a pixel-specific instance. /// -public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProvider +public abstract partial class Image : IDisposable, IConfigurationProvider { private bool isDisposed; private readonly Configuration configuration; @@ -22,20 +22,22 @@ public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProv /// /// Initializes a new instance of the class. /// - /// - /// The configuration which allows altering default behaviour or extending the library. - /// + /// The global configuration.. /// The pixel type information. /// The image metadata. /// The size in px units. protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata? metadata, Size size) - : base(pixelType, size, metadata) - => this.configuration = configuration; + { + this.configuration = configuration; + this.PixelType = pixelType; + this.Size = size; + this.Metadata = metadata ?? new ImageMetadata(); + } /// /// Initializes a new instance of the class. /// - /// The configuration. + /// The global configuration. /// The . /// The . /// The width in px units. @@ -50,6 +52,39 @@ public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProv { } + /// + Configuration IConfigurationProvider.Configuration => this.configuration; + + /// + /// Gets information about the image pixels. + /// + public PixelTypeInfo PixelType { get; } + + /// + /// Gets the image width in px units. + /// + public int Width => this.Size.Width; + + /// + /// Gets the image height in px units. + /// + public int Height => this.Size.Height; + + /// + /// Gets any metadata associated with the image. + /// + public ImageMetadata Metadata { get; } + + /// + /// Gets the size of the image in px units. + /// + public Size Size { get; internal set; } + + /// + /// Gets the bounds of the image. + /// + public Rectangle Bounds => new(0, 0, this.Width, this.Height); + /// /// Gets the implementing the public property. /// @@ -60,9 +95,6 @@ public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProv /// public ImageFrameCollection Frames => this.NonGenericFrameCollection; - /// - Configuration IConfigurationProvider.Configuration => this.configuration; - /// public void Dispose() { diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index 04930a2689..cf970b3166 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Text; @@ -180,6 +179,6 @@ public static partial class ImageExtensions // Always available. stream.TryGetBuffer(out ArraySegment buffer); - return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(buffer.Array, 0, (int)stream.Length)}"; + return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(buffer.Array ?? Array.Empty(), 0, (int)stream.Length)}"; } } diff --git a/src/ImageSharp/ImageFrameCollectionExtensions.cs b/src/ImageSharp/ImageFrameCollectionExtensions.cs new file mode 100644 index 0000000000..660352c159 --- /dev/null +++ b/src/ImageSharp/ImageFrameCollectionExtensions.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp; + +/// +/// Extension methods for . +/// +public static class ImageFrameCollectionExtensions +{ + /// + public static IEnumerable> AsEnumerable(this ImageFrameCollection source) + where TPixel : unmanaged, IPixel + => source; + + /// + public static IEnumerable Select(this ImageFrameCollection source, Func, TResult> selector) + where TPixel : unmanaged, IPixel => source.AsEnumerable().Select(selector); +} diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index fb4ee6ae5f..3734402d30 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -145,7 +144,7 @@ public sealed class ImageFrame : ImageFrame, IPixelSource } /// - public Buffer2D PixelBuffer { get; private set; } + public Buffer2D PixelBuffer { get; } /// /// Gets or sets the pixel at the specified position. @@ -183,7 +182,7 @@ public sealed class ImageFrame : ImageFrame, IPixelSource try { - var accessor = new PixelAccessor(this.PixelBuffer); + PixelAccessor accessor = new(this.PixelBuffer); processPixels(accessor); } finally @@ -211,8 +210,8 @@ public sealed class ImageFrame : ImageFrame, IPixelSource try { - var accessor1 = new PixelAccessor(this.PixelBuffer); - var accessor2 = new PixelAccessor(frame2.PixelBuffer); + PixelAccessor accessor1 = new(this.PixelBuffer); + PixelAccessor accessor2 = new(frame2.PixelBuffer); processPixels(accessor1, accessor2); } finally @@ -247,9 +246,9 @@ public sealed class ImageFrame : ImageFrame, IPixelSource try { - var accessor1 = new PixelAccessor(this.PixelBuffer); - var accessor2 = new PixelAccessor(frame2.PixelBuffer); - var accessor3 = new PixelAccessor(frame3.PixelBuffer); + PixelAccessor accessor1 = new(this.PixelBuffer); + PixelAccessor accessor2 = new(frame2.PixelBuffer); + PixelAccessor accessor3 = new(frame3.PixelBuffer); processPixels(accessor1, accessor2, accessor3); } finally @@ -310,6 +309,7 @@ public sealed class ImageFrame : ImageFrame, IPixelSource /// Copies the pixels to a of the same size. /// /// The target pixel buffer accessor. + /// ImageFrame{TPixel}.CopyTo(): target must be of the same size! internal void CopyTo(Buffer2D target) { if (this.Size() != target.Size()) @@ -342,8 +342,7 @@ public sealed class ImageFrame : ImageFrame, IPixelSource if (disposing) { - this.PixelBuffer?.Dispose(); - this.PixelBuffer = null; + this.PixelBuffer.Dispose(); } this.isDisposed = true; @@ -379,14 +378,14 @@ public sealed class ImageFrame : ImageFrame, IPixelSource /// /// The configuration providing initialization code which allows extending the library. /// The - internal ImageFrame Clone(Configuration configuration) => new ImageFrame(configuration, this); + internal ImageFrame Clone(Configuration configuration) => new(configuration, this); /// /// Returns a copy of the image frame in the given pixel format. /// /// The pixel format. /// The - internal ImageFrame CloneAs() + internal ImageFrame? CloneAs() where TPixel2 : unmanaged, IPixel => this.CloneAs(this.GetConfiguration()); /// @@ -400,11 +399,11 @@ public sealed class ImageFrame : ImageFrame, IPixelSource { if (typeof(TPixel2) == typeof(TPixel)) { - return this.Clone(configuration) as ImageFrame; + return (this.Clone(configuration) as ImageFrame)!; } - var target = new ImageFrame(configuration, this.Width, this.Height, this.Metadata.DeepClone()); - var operation = new RowIntervalOperation(this.PixelBuffer, target.PixelBuffer, configuration); + ImageFrame target = new(configuration, this.Width, this.Height, this.Metadata.DeepClone()); + RowIntervalOperation operation = new(this.PixelBuffer, target.PixelBuffer, configuration); ParallelRowIterator.IterateRowIntervals( configuration, @@ -447,14 +446,12 @@ public sealed class ImageFrame : ImageFrame, IPixelSource } [MethodImpl(InliningOptions.ColdPath)] - private static void ThrowArgumentOutOfRangeException(string paramName) - { - throw new ArgumentOutOfRangeException(paramName); - } + private static void ThrowArgumentOutOfRangeException(string paramName) => throw new ArgumentOutOfRangeException(paramName); /// /// A implementing the clone logic for . /// + /// The type of the target pixel format. private readonly struct RowIntervalOperation : IRowIntervalOperation where TPixel2 : unmanaged, IPixel { diff --git a/src/ImageSharp/ImageInfo.cs b/src/ImageSharp/ImageInfo.cs index fdc15a8127..00319e9b55 100644 --- a/src/ImageSharp/ImageInfo.cs +++ b/src/ImageSharp/ImageInfo.cs @@ -15,11 +15,13 @@ public class ImageInfo /// Initializes a new instance of the class. /// /// The pixel type information. - /// The width of the image in px units. - /// The height of the image in px units. + /// The size of the image in px units. /// The image metadata. - public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetadata? metadata) - : this(pixelType, new(width, height), metadata) + public ImageInfo( + PixelTypeInfo pixelType, + Size size, + ImageMetadata? metadata) + : this(pixelType, size, metadata, null) { } @@ -29,11 +31,17 @@ public class ImageInfo /// The pixel type information. /// The size of the image in px units. /// The image metadata. - public ImageInfo(PixelTypeInfo pixelType, Size size, ImageMetadata? metadata) + /// The collection of image frame metadata. + public ImageInfo( + PixelTypeInfo pixelType, + Size size, + ImageMetadata? metadata, + IReadOnlyList? frameMetadataCollection) { this.PixelType = pixelType; - this.Metadata = metadata ?? new ImageMetadata(); this.Size = size; + this.Metadata = metadata ?? new ImageMetadata(); + this.FrameMetadataCollection = frameMetadataCollection ?? Array.Empty(); } /// @@ -52,10 +60,15 @@ public class ImageInfo public int Height => this.Size.Height; /// - /// Gets any metadata associated wit The image. + /// Gets any metadata associated with the image. /// public ImageMetadata Metadata { get; } + /// + /// Gets the collection of metadata associated with individual image frames. + /// + public IReadOnlyList FrameMetadataCollection { get; } + /// /// Gets the size of the image in px units. /// diff --git a/src/ImageSharp/IndexedImageFrame{TPixel}.cs b/src/ImageSharp/IndexedImageFrame{TPixel}.cs index 741b3219e0..6807e77ad2 100644 --- a/src/ImageSharp/IndexedImageFrame{TPixel}.cs +++ b/src/ImageSharp/IndexedImageFrame{TPixel}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Runtime.CompilerServices; @@ -18,8 +17,8 @@ namespace SixLabors.ImageSharp; public sealed class IndexedImageFrame : IPixelSource, IDisposable where TPixel : unmanaged, IPixel { - private Buffer2D pixelBuffer; - private IMemoryOwner paletteOwner; + private readonly Buffer2D pixelBuffer; + private readonly IMemoryOwner paletteOwner; private bool isDisposed; /// @@ -109,8 +108,6 @@ public sealed class IndexedImageFrame : IPixelSource, IDisposable this.isDisposed = true; this.pixelBuffer.Dispose(); this.paletteOwner.Dispose(); - this.pixelBuffer = null; - this.paletteOwner = null; } } } diff --git a/src/ImageSharp/Memory/Allocators/Internals/ManagedBufferBase.cs b/src/ImageSharp/Memory/Allocators/Internals/ManagedBufferBase.cs index a18bd34474..a6ed797d6d 100644 --- a/src/ImageSharp/Memory/Allocators/Internals/ManagedBufferBase.cs +++ b/src/ImageSharp/Memory/Allocators/Internals/ManagedBufferBase.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Buffers; diff --git a/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs index 2cb4421d5e..798edf9b22 100644 --- a/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs @@ -156,7 +156,7 @@ internal sealed class UniformUnmanagedMemoryPoolMemoryAllocator : MemoryAllocato // Workaround for https://github.com/dotnet/runtime/issues/65466 if (total > 0) { - return total / 8; + return (long)((ulong)total / 8); } } diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 8d6465389f..1173e02e17 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -55,6 +55,8 @@ public sealed class Buffer2D : IDisposable /// internal MemoryGroup FastMemoryGroup { get; private set; } + internal bool IsDisposed { get; private set; } + /// /// Gets a reference to the element at the specified position. /// @@ -79,7 +81,11 @@ public sealed class Buffer2D : IDisposable /// /// Disposes the instance /// - public void Dispose() => this.FastMemoryGroup.Dispose(); + public void Dispose() + { + this.FastMemoryGroup.Dispose(); + this.IsDisposed = true; + } /// /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs index d63fd2076c..9695c7f98f 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs @@ -252,7 +252,7 @@ internal abstract partial class MemoryGroup : IMemoryGroup, IDisposable { ref byte b0 = ref MemoryMarshal.GetReference(this.memoryGroupSpanCache.SingleArray); ref T e0 = ref Unsafe.As(ref b0); - e0 = ref Unsafe.Add(ref e0, y * width); + e0 = ref Unsafe.Add(ref e0, (uint)(y * width)); return MemoryMarshal.CreateSpan(ref e0, width); } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 885db3a5e9..953ef74afb 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -86,10 +86,6 @@ internal class ExifReader : BaseExifReader /// internal abstract class BaseExifReader { - private readonly byte[] buf8 = new byte[8]; - private readonly byte[] buf4 = new byte[4]; - private readonly byte[] buf2 = new byte[2]; - private readonly MemoryAllocator? allocator; private readonly Stream data; private List? invalidTags; @@ -528,20 +524,33 @@ internal abstract class BaseExifReader return read == length; } - protected ulong ReadUInt64() => - this.TryReadSpan(this.buf8) - ? this.ConvertToUInt64(this.buf8) + protected ulong ReadUInt64() + { + Span buffer = stackalloc byte[8]; + + return this.TryReadSpan(buffer) + ? this.ConvertToUInt64(buffer) : default; + } // Known as Long in Exif Specification. - protected uint ReadUInt32() => - this.TryReadSpan(this.buf4) - ? this.ConvertToUInt32(this.buf4) + protected uint ReadUInt32() + { + Span buffer = stackalloc byte[4]; + + return this.TryReadSpan(buffer) + ? this.ConvertToUInt32(buffer) : default; + } - protected ushort ReadUInt16() => this.TryReadSpan(this.buf2) - ? this.ConvertToShort(this.buf2) + protected ushort ReadUInt16() + { + Span buffer = stackalloc byte[2]; + + return this.TryReadSpan(buffer) + ? this.ConvertToShort(buffer) : default; + } private long ConvertToInt64(ReadOnlySpan buffer) { diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs index 4320cb5e82..6811fc6f9c 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs @@ -45,12 +45,12 @@ internal sealed class ExifByteArray : ExifArrayValue private bool TrySetSignedIntArray(int[] intArrayValue) { - if (Array.FindIndex(intArrayValue, x => x < byte.MinValue || x > byte.MaxValue) > -1) + if (Array.FindIndex(intArrayValue, x => (uint)x > byte.MaxValue) >= 0) { return false; } - var value = new byte[intArrayValue.Length]; + byte[] value = new byte[intArrayValue.Length]; for (int i = 0; i < intArrayValue.Length; i++) { int s = intArrayValue[i]; diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs index bbb55b9100..28d8f0a2f5 100644 --- a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs @@ -108,10 +108,10 @@ public sealed class IccProfile : IDeepCloneable const int profileIdPos = 84; // need to copy some values because they need to be zero for the hashing - byte[] temp = new byte[24]; - Buffer.BlockCopy(data, profileFlagPos, temp, 0, 4); - Buffer.BlockCopy(data, renderingIntentPos, temp, 4, 4); - Buffer.BlockCopy(data, profileIdPos, temp, 8, 16); + Span temp = stackalloc byte[24]; + data.AsSpan(profileFlagPos, 4).CopyTo(temp); + data.AsSpan(renderingIntentPos, 4).CopyTo(temp.Slice(4)); + data.AsSpan(profileIdPos, 16).CopyTo(temp.Slice(8)); try { @@ -131,9 +131,9 @@ public sealed class IccProfile : IDeepCloneable } finally { - Buffer.BlockCopy(temp, 0, data, profileFlagPos, 4); - Buffer.BlockCopy(temp, 4, data, renderingIntentPos, 4); - Buffer.BlockCopy(temp, 8, data, profileIdPos, 16); + temp.Slice(0, 4).CopyTo(data.AsSpan(profileFlagPos)); + temp.Slice(4, 4).CopyTo(data.AsSpan(renderingIntentPos)); + temp.Slice(8, 16).CopyTo(data.AsSpan(profileIdPos)); } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index cf19101211..0792306760 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -3,6 +3,10 @@ // using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; @@ -43,18 +47,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -81,18 +152,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -119,18 +257,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -157,18 +362,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -195,18 +467,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -233,18 +572,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -271,18 +677,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -309,18 +782,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -347,18 +887,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrc(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -385,18 +992,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -423,18 +1097,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -461,18 +1202,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -499,18 +1307,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -537,18 +1412,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -575,18 +1517,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -613,18 +1622,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -651,18 +1727,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -689,18 +1832,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -727,18 +1937,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -765,21 +2042,88 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); - } - } - } + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + } + } /// /// A pixel blender that implements the "AddSrcOver" composition equation. @@ -803,18 +2147,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -841,18 +2252,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -879,18 +2357,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -917,18 +2462,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -955,18 +2567,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -993,18 +2672,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1031,18 +2777,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1069,18 +2882,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1107,18 +2987,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1145,18 +3092,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1183,18 +3197,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1221,18 +3302,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1259,18 +3407,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1297,18 +3512,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1335,18 +3617,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1373,18 +3722,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1411,18 +3827,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1449,18 +3932,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1487,18 +4037,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1525,18 +4142,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1563,18 +4247,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1601,18 +4352,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1639,18 +4457,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1677,18 +4562,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlaySrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1715,18 +4667,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightSrcOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1753,18 +4772,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1791,18 +4877,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1829,18 +4982,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1867,18 +5087,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1905,18 +5192,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1943,18 +5297,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -1981,18 +5402,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2019,18 +5507,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2057,18 +5612,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDest(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2095,18 +5717,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2133,18 +5822,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2171,18 +5927,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2209,18 +6032,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2247,18 +6137,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2285,18 +6242,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2323,18 +6347,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2361,18 +6452,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2399,18 +6557,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDestAtop(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2437,18 +6662,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2475,18 +6767,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2513,18 +6872,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2551,18 +6977,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2589,18 +7082,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2627,18 +7187,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2665,18 +7292,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2703,18 +7397,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2741,18 +7502,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDestOver(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2779,18 +7607,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2817,18 +7712,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2855,18 +7817,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2893,18 +7922,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2931,18 +8027,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -2969,18 +8132,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3007,18 +8237,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3045,18 +8342,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3083,18 +8447,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDestIn(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3121,18 +8552,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3159,18 +8657,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3197,18 +8762,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3235,18 +8867,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3273,18 +8972,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3311,18 +9077,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3349,18 +9182,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3387,18 +9287,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3425,18 +9392,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightDestOut(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3463,18 +9497,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3501,18 +9602,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3539,18 +9707,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3577,18 +9812,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3615,18 +9917,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3653,18 +10022,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3691,18 +10127,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3729,18 +10232,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3767,18 +10337,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightClear(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3805,18 +10442,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.NormalXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3843,18 +10547,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.MultiplyXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3881,18 +10652,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.AddXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3919,18 +10757,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.SubtractXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3957,18 +10862,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.ScreenXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -3995,18 +10967,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.DarkenXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -4033,18 +11072,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.LightenXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -4071,18 +11177,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.OverlayXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } @@ -4109,18 +11282,85 @@ internal static class DefaultPixelBlenders protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); + } + } + else { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.HardLightXor(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index 7bd51439ce..da6208eaa2 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -13,6 +13,10 @@ // using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; @@ -86,18 +90,85 @@ var blenders = new []{ protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0, 1); - for (int i = 0; i < destination.Length; i++) + + if (Avx2.IsSupported && destination.Length >= 2) { - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + Vector256 opacity = Vector256.Create(amount); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + destinationBase = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); + } + } + else + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); + } } } /// protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { - for (int i = 0; i < destination.Length; i++) + if (Avx2.IsSupported && destination.Length >= 2) + { + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 destinationBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); + ref Vector256 destinationLast = ref Unsafe.Add(ref destinationBase, (uint)destination.Length / 2u); + + ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref float amountBase = ref MemoryMarshal.GetReference(amount); + + Vector256 vOne = Vector256.Create(1F); + + while (Unsafe.IsAddressLessThan(ref destinationBase, ref destinationLast)) + { + // We need to create a Vector256 containing the current and next amount values + // taking up each half of the Vector256 and then clamp them. + Vector256 opacity = Vector256.Create( + Vector128.Create(amountBase), + Vector128.Create(Unsafe.Add(ref amountBase, 1))); + opacity = Avx.Min(Avx.Max(Vector256.Zero, opacity), vOne); + + destinationBase = PorterDuffFunctions.<#=blender_composer#>(backgroundBase, sourceBase, opacity); + destinationBase = ref Unsafe.Add(ref destinationBase, 1); + backgroundBase = ref Unsafe.Add(ref backgroundBase, 1); + sourceBase = ref Unsafe.Add(ref sourceBase, 1); + amountBase = ref Unsafe.Add(ref amountBase, 2); + } + + if (Numerics.Modulo2(destination.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + int i = destination.Length - 1; + destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } + } + else { - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1)); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], Numerics.Clamp(amount[i], 0, 1F)); + } } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index ff41e70b20..e7cf3b2921 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -5,6 +5,8 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; @@ -21,11 +23,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 NormalSrc(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return source; } + /// + /// Returns the result of the "NormalSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + /// /// Returns the result of the "NormalSrcAtop" compositing equation. /// @@ -36,7 +49,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 NormalSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Normal(backdrop, source)); + } + + /// + /// Returns the result of the "NormalSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(backdrop, source, Normal(backdrop, source)); } @@ -51,7 +79,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 NormalSrcOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Normal(backdrop, source)); + } + + /// + /// Returns the result of the "NormalSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(backdrop, source, Normal(backdrop, source)); } @@ -66,11 +109,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 NormalSrcIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(backdrop, source); } + /// + /// Returns the result of the "NormalSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "NormalSrcOut" compositing equation. /// @@ -81,11 +135,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 NormalSrcOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(backdrop, source); } + /// + /// Returns the result of the "NormalSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "NormalDest" compositing equation. /// @@ -99,6 +164,19 @@ internal static partial class PorterDuffFunctions return backdrop; } + /// + /// Returns the result of the "NormalDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + /// /// Returns the result of the "NormalDestAtop" compositing equation. /// @@ -109,7 +187,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 NormalDestAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Normal(source, backdrop)); + } + + /// + /// Returns the result of the "NormalDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(source, backdrop, Normal(source, backdrop)); } @@ -124,7 +217,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 NormalDestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Normal(source, backdrop)); + } + + /// + /// Returns the result of the "NormalDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(source, backdrop, Normal(source, backdrop)); } @@ -139,11 +247,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 NormalDestIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(source, backdrop); } + /// + /// Returns the result of the "NormalDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "NormalDestOut" compositing equation. /// @@ -154,11 +273,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 NormalDestOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(source, backdrop); } + /// + /// Returns the result of the "NormalDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "NormalXor" compositing equation. /// @@ -169,11 +299,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 NormalXor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Xor(backdrop, source); } + /// + /// Returns the result of the "NormalXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "NormalClear" compositing equation. /// @@ -184,11 +325,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 NormalClear(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Clear(backdrop, source); } + /// + /// Returns the result of the "NormalClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 NormalClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "NormalSrc" compositing equation. @@ -416,11 +568,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 MultiplySrc(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return source; } + /// + /// Returns the result of the "MultiplySrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + /// /// Returns the result of the "MultiplySrcAtop" compositing equation. /// @@ -431,7 +594,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 MultiplySrcAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the result of the "MultiplySrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(backdrop, source, Multiply(backdrop, source)); } @@ -446,7 +624,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 MultiplySrcOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Multiply(backdrop, source)); + } + + /// + /// Returns the result of the "MultiplySrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(backdrop, source, Multiply(backdrop, source)); } @@ -461,11 +654,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 MultiplySrcIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(backdrop, source); } + /// + /// Returns the result of the "MultiplySrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "MultiplySrcOut" compositing equation. /// @@ -476,11 +680,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 MultiplySrcOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(backdrop, source); } + /// + /// Returns the result of the "MultiplySrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplySrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "MultiplyDest" compositing equation. /// @@ -494,6 +709,19 @@ internal static partial class PorterDuffFunctions return backdrop; } + /// + /// Returns the result of the "MultiplyDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + /// /// Returns the result of the "MultiplyDestAtop" compositing equation. /// @@ -504,7 +732,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 MultiplyDestAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the result of the "MultiplyDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(source, backdrop, Multiply(source, backdrop)); } @@ -519,7 +762,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 MultiplyDestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Multiply(source, backdrop)); + } + + /// + /// Returns the result of the "MultiplyDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(source, backdrop, Multiply(source, backdrop)); } @@ -534,11 +792,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 MultiplyDestIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(source, backdrop); } + /// + /// Returns the result of the "MultiplyDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "MultiplyDestOut" compositing equation. /// @@ -549,11 +818,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 MultiplyDestOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(source, backdrop); } + /// + /// Returns the result of the "MultiplyDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "MultiplyXor" compositing equation. /// @@ -564,11 +844,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 MultiplyXor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Xor(backdrop, source); } + /// + /// Returns the result of the "MultiplyXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "MultiplyClear" compositing equation. /// @@ -579,11 +870,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 MultiplyClear(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Clear(backdrop, source); } + /// + /// Returns the result of the "MultiplyClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 MultiplyClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "MultiplySrc" compositing equation. @@ -811,11 +1113,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 AddSrc(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return source; } + /// + /// Returns the result of the "AddSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + /// /// Returns the result of the "AddSrcAtop" compositing equation. /// @@ -826,65 +1139,130 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 AddSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Atop(backdrop, source, Add(backdrop, source)); } /// - /// Returns the result of the "AddSrcOver" compositing equation. + /// Returns the result of the "AddSrcAtop" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 - /// The . + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 AddSrcOver(Vector4 backdrop, Vector4 source, float opacity) + public static Vector256 AddSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source.W *= opacity; + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); - return Over(backdrop, source, Add(backdrop, source)); + return Atop(backdrop, source, Add(backdrop, source)); } /// - /// Returns the result of the "AddSrcIn" compositing equation. + /// Returns the result of the "AddSrcOver" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 AddSrcIn(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 AddSrcOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); - return In(backdrop, source); + return Over(backdrop, source, Add(backdrop, source)); } /// - /// Returns the result of the "AddSrcOut" compositing equation. + /// Returns the result of the "AddSrcOver" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 - /// The . + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 AddSrcOut(Vector4 backdrop, Vector4 source, float opacity) + public static Vector256 AddSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source.W *= opacity; + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); - return Out(backdrop, source); + return Over(backdrop, source, Add(backdrop, source)); } /// - /// Returns the result of the "AddDest" compositing equation. + /// Returns the result of the "AddSrcIn" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 AddDest(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 AddSrcIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(backdrop, source); + } + + /// + /// Returns the result of the "AddSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + + /// + /// Returns the result of the "AddSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddSrcOut(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Out(backdrop, source); + } + + /// + /// Returns the result of the "AddSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + + /// + /// Returns the result of the "AddDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddDest(Vector4 backdrop, Vector4 source, float opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "AddDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDest(Vector256 backdrop, Vector256 source, Vector256 opacity) { return backdrop; } @@ -899,7 +1277,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 AddDestAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the result of the "AddDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(source, backdrop, Add(source, backdrop)); } @@ -914,7 +1307,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 AddDestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Add(source, backdrop)); + } + + /// + /// Returns the result of the "AddDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(source, backdrop, Add(source, backdrop)); } @@ -929,11 +1337,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 AddDestIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(source, backdrop); } + /// + /// Returns the result of the "AddDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "AddDestOut" compositing equation. /// @@ -944,11 +1363,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 AddDestOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(source, backdrop); } + /// + /// Returns the result of the "AddDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "AddXor" compositing equation. /// @@ -959,11 +1389,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 AddXor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Xor(backdrop, source); } + /// + /// Returns the result of the "AddXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "AddClear" compositing equation. /// @@ -974,11 +1415,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 AddClear(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Clear(backdrop, source); } + /// + /// Returns the result of the "AddClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 AddClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "AddSrc" compositing equation. @@ -1206,11 +1658,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 SubtractSrc(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return source; } + /// + /// Returns the result of the "SubtractSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + /// /// Returns the result of the "SubtractSrcAtop" compositing equation. /// @@ -1221,7 +1684,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 SubtractSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the result of the "SubtractSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(backdrop, source, Subtract(backdrop, source)); } @@ -1236,7 +1714,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 SubtractSrcOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Subtract(backdrop, source)); + } + + /// + /// Returns the result of the "SubtractSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(backdrop, source, Subtract(backdrop, source)); } @@ -1251,11 +1744,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 SubtractSrcIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(backdrop, source); } + /// + /// Returns the result of the "SubtractSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "SubtractSrcOut" compositing equation. /// @@ -1266,11 +1770,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 SubtractSrcOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(backdrop, source); } + /// + /// Returns the result of the "SubtractSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "SubtractDest" compositing equation. /// @@ -1284,6 +1799,19 @@ internal static partial class PorterDuffFunctions return backdrop; } + /// + /// Returns the result of the "SubtractDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + /// /// Returns the result of the "SubtractDestAtop" compositing equation. /// @@ -1294,7 +1822,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 SubtractDestAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the result of the "SubtractDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(source, backdrop, Subtract(source, backdrop)); } @@ -1309,7 +1852,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 SubtractDestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Subtract(source, backdrop)); + } + + /// + /// Returns the result of the "SubtractDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(source, backdrop, Subtract(source, backdrop)); } @@ -1324,11 +1882,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 SubtractDestIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(source, backdrop); } + /// + /// Returns the result of the "SubtractDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "SubtractDestOut" compositing equation. /// @@ -1339,11 +1908,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 SubtractDestOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(source, backdrop); } + /// + /// Returns the result of the "SubtractDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "SubtractXor" compositing equation. /// @@ -1354,11 +1934,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 SubtractXor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Xor(backdrop, source); } + /// + /// Returns the result of the "SubtractXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "SubtractClear" compositing equation. /// @@ -1369,11 +1960,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 SubtractClear(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Clear(backdrop, source); } + /// + /// Returns the result of the "SubtractClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 SubtractClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "SubtractSrc" compositing equation. @@ -1601,11 +2203,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 ScreenSrc(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return source; } + /// + /// Returns the result of the "ScreenSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + /// /// Returns the result of the "ScreenSrcAtop" compositing equation. /// @@ -1616,7 +2229,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 ScreenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the result of the "ScreenSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(backdrop, source, Screen(backdrop, source)); } @@ -1631,7 +2259,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 ScreenSrcOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Screen(backdrop, source)); + } + + /// + /// Returns the result of the "ScreenSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(backdrop, source, Screen(backdrop, source)); } @@ -1646,11 +2289,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 ScreenSrcIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(backdrop, source); } + /// + /// Returns the result of the "ScreenSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "ScreenSrcOut" compositing equation. /// @@ -1661,11 +2315,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 ScreenSrcOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(backdrop, source); } + /// + /// Returns the result of the "ScreenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "ScreenDest" compositing equation. /// @@ -1679,19 +2344,62 @@ internal static partial class PorterDuffFunctions return backdrop; } + /// + /// Returns the result of the "ScreenDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + + /// + /// Returns the result of the "ScreenDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenDestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Screen(source, backdrop)); + } + /// /// Returns the result of the "ScreenDestAtop" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + + return Atop(source, backdrop, Screen(source, backdrop)); + } + + /// + /// Returns the result of the "ScreenDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 ScreenDestAtop(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 ScreenDestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); - return Atop(source, backdrop, Screen(source, backdrop)); + return Over(source, backdrop, Screen(source, backdrop)); } /// @@ -1700,11 +2408,11 @@ internal static partial class PorterDuffFunctions /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 - /// The . + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 ScreenDestOver(Vector4 backdrop, Vector4 source, float opacity) + public static Vector256 ScreenDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) { - source.W *= opacity; + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(source, backdrop, Screen(source, backdrop)); } @@ -1719,11 +2427,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 ScreenDestIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(source, backdrop); } + /// + /// Returns the result of the "ScreenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "ScreenDestOut" compositing equation. /// @@ -1734,11 +2453,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 ScreenDestOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(source, backdrop); } + /// + /// Returns the result of the "ScreenDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "ScreenXor" compositing equation. /// @@ -1749,11 +2479,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 ScreenXor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Xor(backdrop, source); } + /// + /// Returns the result of the "ScreenXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "ScreenClear" compositing equation. /// @@ -1764,11 +2505,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 ScreenClear(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Clear(backdrop, source); } + /// + /// Returns the result of the "ScreenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 ScreenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "ScreenSrc" compositing equation. @@ -1996,11 +2748,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DarkenSrc(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return source; } + /// + /// Returns the result of the "DarkenSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + /// /// Returns the result of the "DarkenSrcAtop" compositing equation. /// @@ -2011,7 +2774,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DarkenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the result of the "DarkenSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(backdrop, source, Darken(backdrop, source)); } @@ -2026,7 +2804,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DarkenSrcOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Darken(backdrop, source)); + } + + /// + /// Returns the result of the "DarkenSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(backdrop, source, Darken(backdrop, source)); } @@ -2041,11 +2834,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DarkenSrcIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(backdrop, source); } + /// + /// Returns the result of the "DarkenSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "DarkenSrcOut" compositing equation. /// @@ -2056,11 +2860,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DarkenSrcOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(backdrop, source); } + /// + /// Returns the result of the "DarkenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "DarkenDest" compositing equation. /// @@ -2074,6 +2889,19 @@ internal static partial class PorterDuffFunctions return backdrop; } + /// + /// Returns the result of the "DarkenDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + /// /// Returns the result of the "DarkenDestAtop" compositing equation. /// @@ -2084,7 +2912,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DarkenDestAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the result of the "DarkenDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(source, backdrop, Darken(source, backdrop)); } @@ -2099,7 +2942,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DarkenDestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Darken(source, backdrop)); + } + + /// + /// Returns the result of the "DarkenDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(source, backdrop, Darken(source, backdrop)); } @@ -2114,11 +2972,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DarkenDestIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(source, backdrop); } + /// + /// Returns the result of the "DarkenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "DarkenDestOut" compositing equation. /// @@ -2129,11 +2998,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DarkenDestOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(source, backdrop); } + /// + /// Returns the result of the "DarkenDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "DarkenXor" compositing equation. /// @@ -2144,11 +3024,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DarkenXor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Xor(backdrop, source); } + /// + /// Returns the result of the "DarkenXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "DarkenClear" compositing equation. /// @@ -2159,11 +3050,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 DarkenClear(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Clear(backdrop, source); } + /// + /// Returns the result of the "DarkenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DarkenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "DarkenSrc" compositing equation. @@ -2391,11 +3293,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 LightenSrc(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return source; } + /// + /// Returns the result of the "LightenSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + /// /// Returns the result of the "LightenSrcAtop" compositing equation. /// @@ -2406,7 +3319,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 LightenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the result of the "LightenSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(backdrop, source, Lighten(backdrop, source)); } @@ -2421,7 +3349,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 LightenSrcOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Lighten(backdrop, source)); + } + + /// + /// Returns the result of the "LightenSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(backdrop, source, Lighten(backdrop, source)); } @@ -2436,11 +3379,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 LightenSrcIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(backdrop, source); } + /// + /// Returns the result of the "LightenSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "LightenSrcOut" compositing equation. /// @@ -2451,11 +3405,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 LightenSrcOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(backdrop, source); } + /// + /// Returns the result of the "LightenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "LightenDest" compositing equation. /// @@ -2469,6 +3434,19 @@ internal static partial class PorterDuffFunctions return backdrop; } + /// + /// Returns the result of the "LightenDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + /// /// Returns the result of the "LightenDestAtop" compositing equation. /// @@ -2479,7 +3457,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 LightenDestAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the result of the "LightenDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(source, backdrop, Lighten(source, backdrop)); } @@ -2494,24 +3487,65 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 LightenDestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Lighten(source, backdrop)); + } + + /// + /// Returns the result of the "LightenDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(source, backdrop, Lighten(source, backdrop)); } /// - /// Returns the result of the "LightenDestIn" compositing equation. + /// Returns the result of the "LightenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenDestIn(Vector4 backdrop, Vector4 source, float opacity) + { + source = Numerics.WithW(source, source * opacity); + + return In(source, backdrop); + } + + /// + /// Returns the result of the "LightenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + + /// + /// Returns the result of the "LightenDestOut" compositing equation. /// /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 LightenDestIn(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 LightenDestOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); - return In(source, backdrop); + return Out(source, backdrop); } /// @@ -2520,14 +3554,10 @@ internal static partial class PorterDuffFunctions /// The backdrop vector. /// The source vector. /// The source opacity. Range 0..1 - /// The . + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 LightenDestOut(Vector4 backdrop, Vector4 source, float opacity) - { - source.W *= opacity; - - return Out(source, backdrop); - } + public static Vector256 LightenDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); /// /// Returns the result of the "LightenXor" compositing equation. @@ -2539,11 +3569,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 LightenXor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Xor(backdrop, source); } + /// + /// Returns the result of the "LightenXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "LightenClear" compositing equation. /// @@ -2554,11 +3595,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 LightenClear(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Clear(backdrop, source); } + /// + /// Returns the result of the "LightenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 LightenClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "LightenSrc" compositing equation. @@ -2786,11 +3838,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 OverlaySrc(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return source; } + /// + /// Returns the result of the "OverlaySrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + /// /// Returns the result of the "OverlaySrcAtop" compositing equation. /// @@ -2801,7 +3864,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 OverlaySrcAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the result of the "OverlaySrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(backdrop, source, Overlay(backdrop, source)); } @@ -2816,7 +3894,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 OverlaySrcOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, Overlay(backdrop, source)); + } + + /// + /// Returns the result of the "OverlaySrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(backdrop, source, Overlay(backdrop, source)); } @@ -2831,11 +3924,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 OverlaySrcIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(backdrop, source); } + /// + /// Returns the result of the "OverlaySrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "OverlaySrcOut" compositing equation. /// @@ -2846,11 +3950,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 OverlaySrcOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(backdrop, source); } + /// + /// Returns the result of the "OverlaySrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlaySrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "OverlayDest" compositing equation. /// @@ -2864,6 +3979,19 @@ internal static partial class PorterDuffFunctions return backdrop; } + /// + /// Returns the result of the "OverlayDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + /// /// Returns the result of the "OverlayDestAtop" compositing equation. /// @@ -2874,7 +4002,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 OverlayDestAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the result of the "OverlayDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(source, backdrop, Overlay(source, backdrop)); } @@ -2889,7 +4032,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 OverlayDestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, Overlay(source, backdrop)); + } + + /// + /// Returns the result of the "OverlayDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(source, backdrop, Overlay(source, backdrop)); } @@ -2904,11 +4062,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 OverlayDestIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(source, backdrop); } + /// + /// Returns the result of the "OverlayDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "OverlayDestOut" compositing equation. /// @@ -2919,11 +4088,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 OverlayDestOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(source, backdrop); } + /// + /// Returns the result of the "OverlayDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "OverlayXor" compositing equation. /// @@ -2934,11 +4114,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 OverlayXor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Xor(backdrop, source); } + /// + /// Returns the result of the "OverlayXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "OverlayClear" compositing equation. /// @@ -2949,11 +4140,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 OverlayClear(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Clear(backdrop, source); } + /// + /// Returns the result of the "OverlayClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "OverlaySrc" compositing equation. @@ -3181,11 +4383,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLightSrc(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return source; } + /// + /// Returns the result of the "HardLightSrc compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrc(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + /// /// Returns the result of the "HardLightSrcAtop" compositing equation. /// @@ -3196,7 +4409,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLightSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the result of the "HardLightSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(backdrop, source, HardLight(backdrop, source)); } @@ -3211,7 +4439,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLightSrcOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, HardLight(backdrop, source)); + } + + /// + /// Returns the result of the "HardLightSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(backdrop, source, HardLight(backdrop, source)); } @@ -3226,11 +4469,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLightSrcIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(backdrop, source); } + /// + /// Returns the result of the "HardLightSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "HardLightSrcOut" compositing equation. /// @@ -3241,11 +4495,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLightSrcOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(backdrop, source); } + /// + /// Returns the result of the "HardLightSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightSrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "HardLightDest" compositing equation. /// @@ -3259,6 +4524,19 @@ internal static partial class PorterDuffFunctions return backdrop; } + /// + /// Returns the result of the "HardLightDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + /// /// Returns the result of the "HardLightDestAtop" compositing equation. /// @@ -3269,7 +4547,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLightDestAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the result of the "HardLightDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(source, backdrop, HardLight(source, backdrop)); } @@ -3284,7 +4577,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLightDestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, HardLight(source, backdrop)); + } + + /// + /// Returns the result of the "HardLightDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(source, backdrop, HardLight(source, backdrop)); } @@ -3299,11 +4607,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLightDestIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(source, backdrop); } + /// + /// Returns the result of the "HardLightDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "HardLightDestOut" compositing equation. /// @@ -3314,11 +4633,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLightDestOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(source, backdrop); } + /// + /// Returns the result of the "HardLightDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightDestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "HardLightXor" compositing equation. /// @@ -3329,11 +4659,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLightXor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Xor(backdrop, source); } + /// + /// Returns the result of the "HardLightXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightXor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "HardLightClear" compositing equation. /// @@ -3344,11 +4685,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLightClear(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Clear(backdrop, source); } + /// + /// Returns the result of the "HardLightClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLightClear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "HardLightSrc" compositing equation. diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index 40d8b89970..64eee502bb 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -15,6 +15,8 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; @@ -31,11 +33,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return source; } + /// + /// Returns the result of the "<#=blender#>Src compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>Src(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); + /// /// Returns the result of the "<#=blender#>SrcAtop" compositing equation. /// @@ -46,7 +59,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(backdrop, source, <#=blender#>(backdrop, source)); + } + + /// + /// Returns the result of the "<#=blender#>SrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>SrcAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(backdrop, source, <#=blender#>(backdrop, source)); } @@ -61,7 +89,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(backdrop, source, <#=blender#>(backdrop, source)); + } + + /// + /// Returns the result of the "<#=blender#>SrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>SrcOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(backdrop, source, <#=blender#>(backdrop, source)); } @@ -76,11 +119,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(backdrop, source); } + /// + /// Returns the result of the "<#=blender#>SrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>SrcIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "<#=blender#>SrcOut" compositing equation. /// @@ -91,11 +145,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(backdrop, source); } + /// + /// Returns the result of the "<#=blender#>SrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>SrcOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "<#=blender#>Dest" compositing equation. /// @@ -109,6 +174,19 @@ internal static partial class PorterDuffFunctions return backdrop; } + /// + /// Returns the result of the "<#=blender#>Dest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>Dest(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + return backdrop; + } + /// /// Returns the result of the "<#=blender#>DestAtop" compositing equation. /// @@ -119,7 +197,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Atop(source, backdrop, <#=blender#>(source, backdrop)); + } + + /// + /// Returns the result of the "<#=blender#>DestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>DestAtop(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Atop(source, backdrop, <#=blender#>(source, backdrop)); } @@ -134,7 +227,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); + + return Over(source, backdrop, <#=blender#>(source, backdrop)); + } + + /// + /// Returns the result of the "<#=blender#>DestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>DestOver(Vector256 backdrop, Vector256 source, Vector256 opacity) + { + source = Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl); return Over(source, backdrop, <#=blender#>(source, backdrop)); } @@ -149,11 +257,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return In(source, backdrop); } + /// + /// Returns the result of the "<#=blender#>DestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>DestIn(Vector256 backdrop, Vector256 source, Vector256 opacity) + => In(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "<#=blender#>DestOut" compositing equation. /// @@ -164,11 +283,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Out(source, backdrop); } + /// + /// Returns the result of the "<#=blender#>DestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>DestOut(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Out(Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl), backdrop); + /// /// Returns the result of the "<#=blender#>Xor" compositing equation. /// @@ -179,11 +309,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Xor(backdrop, source); } + /// + /// Returns the result of the "<#=blender#>Xor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>Xor(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Xor(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + /// /// Returns the result of the "<#=blender#>Clear" compositing equation. /// @@ -194,11 +335,22 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity) { - source.W *= opacity; + source = Numerics.WithW(source, source * opacity); return Clear(backdrop, source); } + /// + /// Returns the result of the "<#=blender#>Clear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 <#=blender#>Clear(Vector256 backdrop, Vector256 source, Vector256 opacity) + => Clear(backdrop, Avx.Blend(source, Avx.Multiply(source, opacity), BlendAlphaControl)); + <#} #> <# void GenerateGenericPixelBlender(string blender, string composer) { #> diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index 9bc7e35f30..ca358be31c 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -3,6 +3,8 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; @@ -19,6 +21,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders; /// internal static partial class PorterDuffFunctions { + private const int BlendAlphaControl = 0b_10_00_10_00; + private const int ShuffleAlphaControl = 0b_11_11_11_11; + /// /// Returns the result of the "Normal" compositing equation. /// @@ -27,9 +32,17 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Normal(Vector4 backdrop, Vector4 source) - { - return source; - } + => source; + + /// + /// Returns the result of the "Normal" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Normal(Vector256 backdrop, Vector256 source) + => source; /// /// Returns the result of the "Multiply" compositing equation. @@ -39,9 +52,17 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Multiply(Vector4 backdrop, Vector4 source) - { - return backdrop * source; - } + => backdrop * source; + + /// + /// Returns the result of the "Multiply" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Multiply(Vector256 backdrop, Vector256 source) + => Avx.Multiply(backdrop, source); /// /// Returns the result of the "Add" compositing equation. @@ -51,9 +72,17 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Add(Vector4 backdrop, Vector4 source) - { - return Vector4.Min(Vector4.One, backdrop + source); - } + => Vector4.Min(Vector4.One, backdrop + source); + + /// + /// Returns the result of the "Add" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Add(Vector256 backdrop, Vector256 source) + => Avx.Min(Vector256.Create(1F), Avx.Add(backdrop, source)); /// /// Returns the result of the "Subtract" compositing equation. @@ -63,9 +92,17 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Subtract(Vector4 backdrop, Vector4 source) - { - return Vector4.Max(Vector4.Zero, backdrop - source); - } + => Vector4.Max(Vector4.Zero, backdrop - source); + + /// + /// Returns the result of the "Subtract" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Subtract(Vector256 backdrop, Vector256 source) + => Avx.Max(Vector256.Zero, Avx.Subtract(backdrop, source)); /// /// Returns the result of the "Screen" compositing equation. @@ -75,8 +112,19 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Screen(Vector4 backdrop, Vector4 source) + => Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source)); + + /// + /// Returns the result of the "Screen" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Screen(Vector256 backdrop, Vector256 source) { - return Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source)); + Vector256 vOne = Vector256.Create(1F); + return SimdUtils.HwIntrinsics.MultiplyAddNegated(Avx.Subtract(vOne, backdrop), Avx.Subtract(vOne, source), vOne); } /// @@ -87,9 +135,17 @@ internal static partial class PorterDuffFunctions /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Darken(Vector4 backdrop, Vector4 source) - { - return Vector4.Min(backdrop, source); - } + => Vector4.Min(backdrop, source); + + /// + /// Returns the result of the "Darken" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Darken(Vector256 backdrop, Vector256 source) + => Avx.Min(backdrop, source); /// /// Returns the result of the "Lighten" compositing equation. @@ -98,10 +154,17 @@ internal static partial class PorterDuffFunctions /// The source vector. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Lighten(Vector4 backdrop, Vector4 source) - { - return Vector4.Max(backdrop, source); - } + public static Vector4 Lighten(Vector4 backdrop, Vector4 source) => Vector4.Max(backdrop, source); + + /// + /// Returns the result of the "Lighten" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Lighten(Vector256 backdrop, Vector256 source) + => Avx.Max(backdrop, source); /// /// Returns the result of the "Overlay" compositing equation. @@ -119,6 +182,19 @@ internal static partial class PorterDuffFunctions return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0)); } + /// + /// Returns the result of the "Overlay" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Overlay(Vector256 backdrop, Vector256 source) + { + Vector256 color = OverlayValueFunction(backdrop, source); + return Avx.Min(Vector256.Create(1F), Avx.Blend(color, Vector256.Zero, BlendAlphaControl)); + } + /// /// Returns the result of the "HardLight" compositing equation. /// @@ -136,15 +212,44 @@ internal static partial class PorterDuffFunctions } /// - /// Helper function for Overlay andHardLight modes + /// Returns the result of the "HardLight" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 HardLight(Vector256 backdrop, Vector256 source) + { + Vector256 color = OverlayValueFunction(source, backdrop); + return Avx.Min(Vector256.Create(1F), Avx.Blend(color, Vector256.Zero, BlendAlphaControl)); + } + + /// + /// Helper function for Overlay and HardLight modes /// /// Backdrop color element /// Source color element /// Overlay value [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float OverlayValueFunction(float backdrop, float source) + => backdrop <= 0.5f ? (2 * backdrop * source) : 1 - (2 * (1 - source) * (1 - backdrop)); + + /// + /// Helper function for Overlay and HardLight modes + /// + /// Backdrop color element + /// Source color element + /// Overlay value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 OverlayValueFunction(Vector256 backdrop, Vector256 source) { - return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - (2 * (1 - source) * (1 - backdrop)); + Vector256 vOne = Vector256.Create(1F); + Vector256 left = Avx.Multiply(Avx.Add(backdrop, backdrop), source); + + Vector256 vOneMinusSource = Avx.Subtract(vOne, source); + Vector256 right = SimdUtils.HwIntrinsics.MultiplyAddNegated(Avx.Add(vOneMinusSource, vOneMinusSource), Avx.Subtract(vOne, backdrop), vOne); + Vector256 cmp = Avx.CompareGreaterThan(backdrop, Vector256.Create(.5F)); + return Avx.BlendVariable(left, right, cmp); } /// @@ -158,23 +263,54 @@ internal static partial class PorterDuffFunctions public static Vector4 Over(Vector4 destination, Vector4 source, Vector4 blend) { // calculate weights - float blendW = destination.W * source.W; - float dstW = destination.W - blendW; - float srcW = source.W - blendW; + Vector4 sW = Numerics.PermuteW(source); + Vector4 dW = Numerics.PermuteW(destination); + + Vector4 blendW = sW * dW; + Vector4 dstW = dW - blendW; + Vector4 srcW = sW - blendW; // calculate final alpha - float alpha = dstW + source.W; + Vector4 alpha = dstW + sW; // calculate final color Vector4 color = (destination * dstW) + (source * srcW) + (blend * blendW); // unpremultiply - color /= MathF.Max(alpha, Constants.Epsilon); - color.W = alpha; - + Numerics.UnPremultiply(ref color, alpha); return color; } + /// + /// Returns the result of the "Over" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The amount to blend. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Over(Vector256 destination, Vector256 source, Vector256 blend) + { + // calculate weights + Vector256 sW = Avx.Permute(source, ShuffleAlphaControl); + Vector256 dW = Avx.Permute(destination, ShuffleAlphaControl); + + Vector256 blendW = Avx.Multiply(sW, dW); + Vector256 dstW = Avx.Subtract(dW, blendW); + Vector256 srcW = Avx.Subtract(sW, blendW); + + // calculate final alpha + Vector256 alpha = Avx.Add(dstW, sW); + + // calculate final color + Vector256 color = Avx.Multiply(destination, dstW); + color = SimdUtils.HwIntrinsics.MultiplyAdd(color, source, srcW); + color = SimdUtils.HwIntrinsics.MultiplyAdd(color, blend, blendW); + + // unpremultiply + return Numerics.UnPremultiply(color, alpha); + } + /// /// Returns the result of the "Atop" compositing equation. /// @@ -186,22 +322,48 @@ internal static partial class PorterDuffFunctions public static Vector4 Atop(Vector4 destination, Vector4 source, Vector4 blend) { // calculate weights - float blendW = destination.W * source.W; - float dstW = destination.W - blendW; + Vector4 sW = Numerics.PermuteW(source); + Vector4 dW = Numerics.PermuteW(destination); + + Vector4 blendW = sW * dW; + Vector4 dstW = dW - blendW; // calculate final alpha - float alpha = destination.W; + Vector4 alpha = dW; // calculate final color Vector4 color = (destination * dstW) + (blend * blendW); // unpremultiply - color /= MathF.Max(alpha, Constants.Epsilon); - color.W = alpha; - + Numerics.UnPremultiply(ref color, alpha); return color; } + /// + /// Returns the result of the "Atop" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The amount to blend. Range 0..1 + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Atop(Vector256 destination, Vector256 source, Vector256 blend) + { + // calculate final alpha + Vector256 alpha = Avx.Permute(destination, ShuffleAlphaControl); + + // calculate weights + Vector256 sW = Avx.Permute(source, ShuffleAlphaControl); + Vector256 blendW = Avx.Multiply(sW, alpha); + Vector256 dstW = Avx.Subtract(alpha, blendW); + + // calculate final color + Vector256 color = SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(blend, blendW), destination, dstW); + + // unpremultiply + return Numerics.UnPremultiply(color, alpha); + } + /// /// Returns the result of the "In" compositing equation. /// @@ -211,15 +373,34 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 In(Vector4 destination, Vector4 source) { - float alpha = destination.W * source.W; - - Vector4 color = source * alpha; // premultiply - color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply - color.W = alpha; + Vector4 sW = Numerics.PermuteW(source); + Vector4 dW = Numerics.PermuteW(destination); + Vector4 alpha = dW * sW; + Vector4 color = source * alpha; // premultiply + Numerics.UnPremultiply(ref color, alpha); // unpremultiply return color; } + /// + /// Returns the result of the "In" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 In(Vector256 destination, Vector256 source) + { + // calculate alpha + Vector256 alpha = Avx.Permute(Avx.Multiply(source, destination), ShuffleAlphaControl); + + // premultiply + Vector256 color = Avx.Multiply(source, alpha); + + // unpremultiply + return Numerics.UnPremultiply(color, alpha); + } + /// /// Returns the result of the "Out" compositing equation. /// @@ -229,15 +410,34 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Out(Vector4 destination, Vector4 source) { - float alpha = (1 - destination.W) * source.W; - - Vector4 color = source * alpha; // premultiply - color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply - color.W = alpha; + Vector4 sW = Numerics.PermuteW(source); + Vector4 dW = Numerics.PermuteW(destination); + Vector4 alpha = (Vector4.One - dW) * sW; + Vector4 color = source * alpha; // premultiply + Numerics.UnPremultiply(ref color, alpha); // unpremultiply return color; } + /// + /// Returns the result of the "Out" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Out(Vector256 destination, Vector256 source) + { + // calculate alpha + Vector256 alpha = Avx.Permute(Avx.Multiply(source, Avx.Subtract(Vector256.Create(1F), destination)), ShuffleAlphaControl); + + // premultiply + Vector256 color = Avx.Multiply(source, alpha); + + // unpremultiply + return Numerics.UnPremultiply(color, alpha); + } + /// /// Returns the result of the "XOr" compositing equation. /// @@ -247,22 +447,48 @@ internal static partial class PorterDuffFunctions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Xor(Vector4 destination, Vector4 source) { - float srcW = 1 - destination.W; - float dstW = 1 - source.W; + Vector4 sW = Numerics.PermuteW(source); + Vector4 dW = Numerics.PermuteW(destination); - float alpha = (source.W * srcW) + (destination.W * dstW); - Vector4 color = (source.W * source * srcW) + (destination.W * destination * dstW); + Vector4 srcW = Vector4.One - dW; + Vector4 dstW = Vector4.One - sW; - // unpremultiply - color /= MathF.Max(alpha, Constants.Epsilon); - color.W = alpha; + Vector4 alpha = (sW * srcW) + (dW * dstW); + Vector4 color = (sW * source * srcW) + (dW * destination * dstW); + // unpremultiply + Numerics.UnPremultiply(ref color, alpha); return color; } + /// + /// Returns the result of the "XOr" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 Clear(Vector4 backdrop, Vector4 source) + public static Vector256 Xor(Vector256 destination, Vector256 source) { - return Vector4.Zero; + // calculate weights + Vector256 sW = Avx.Shuffle(source, source, ShuffleAlphaControl); + Vector256 dW = Avx.Shuffle(destination, destination, ShuffleAlphaControl); + + Vector256 vOne = Vector256.Create(1F); + Vector256 srcW = Avx.Subtract(vOne, dW); + Vector256 dstW = Avx.Subtract(vOne, sW); + + // calculate alpha + Vector256 alpha = SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(dW, dstW), sW, srcW); + Vector256 color = SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(Avx.Multiply(dW, destination), dstW), Avx.Multiply(sW, source), srcW); + + // unpremultiply + return Numerics.UnPremultiply(color, alpha); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Clear(Vector4 backdrop, Vector4 source) => Vector4.Zero; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 Clear(Vector256 backdrop, Vector256 source) => Vector256.Zero; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index ca68c5aaf9..4891abba8c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -216,7 +216,7 @@ public partial struct Abgr32 : IPixel, IPackedVector { // We can assign the Bgr24 value directly to last three bytes of this instance. ref byte thisRef = ref Unsafe.As(ref this); - ref byte thisRefFromB = ref Unsafe.AddByteOffset(ref thisRef, new IntPtr(1)); + ref byte thisRefFromB = ref Unsafe.AddByteOffset(ref thisRef, 1); Unsafe.As(ref thisRefFromB) = source; this.A = byte.MaxValue; } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index 65b36059b8..aedf4ad198 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -190,7 +190,7 @@ public partial struct Bgr24 : IPixel { // We can assign this instances value directly to last three bytes of the Abgr32. ref byte sourceRef = ref Unsafe.As(ref source); - ref byte sourceRefFromB = ref Unsafe.AddByteOffset(ref sourceRef, new IntPtr(1)); + ref byte sourceRefFromB = ref Unsafe.AddByteOffset(ref sourceRef, 1); this = Unsafe.As(ref sourceRefFromB); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs index cb67f7ffb6..f24ed6fae8 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Abgr32.PixelOperations.Generated.cs @@ -57,7 +57,7 @@ public partial struct Abgr32 { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } - + /// public override void ToRgba32( Configuration configuration, @@ -71,7 +71,7 @@ public partial struct Abgr32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToRgba32(source, dest); } - + /// public override void FromRgba32( Configuration configuration, @@ -84,8 +84,8 @@ public partial struct Abgr32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToAbgr32(source, dest); - } - + } + /// public override void ToArgb32( Configuration configuration, @@ -99,7 +99,7 @@ public partial struct Abgr32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToArgb32(source, dest); } - + /// public override void FromArgb32( Configuration configuration, @@ -112,8 +112,8 @@ public partial struct Abgr32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToAbgr32(source, dest); - } - + } + /// public override void ToBgra32( Configuration configuration, @@ -127,7 +127,7 @@ public partial struct Abgr32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToBgra32(source, dest); } - + /// public override void FromBgra32( Configuration configuration, @@ -140,8 +140,8 @@ public partial struct Abgr32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToAbgr32(source, dest); - } - + } + /// public override void ToRgb24( Configuration configuration, @@ -155,7 +155,7 @@ public partial struct Abgr32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToRgb24(source, dest); } - + /// public override void FromRgb24( Configuration configuration, @@ -168,8 +168,8 @@ public partial struct Abgr32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToAbgr32(source, dest); - } - + } + /// public override void ToBgr24( Configuration configuration, @@ -183,7 +183,7 @@ public partial struct Abgr32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToBgr24(source, dest); } - + /// public override void FromBgr24( Configuration configuration, @@ -196,7 +196,7 @@ public partial struct Abgr32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToAbgr32(source, dest); - } + } /// public override void ToL8( @@ -210,7 +210,7 @@ public partial struct Abgr32 ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -231,7 +231,7 @@ public partial struct Abgr32 ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -252,7 +252,7 @@ public partial struct Abgr32 ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -273,7 +273,7 @@ public partial struct Abgr32 ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -294,7 +294,7 @@ public partial struct Abgr32 ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -315,7 +315,7 @@ public partial struct Abgr32 ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -336,7 +336,7 @@ public partial struct Abgr32 ref Abgr32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Abgr32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs index 5dcca2ad1e..37ac39a851 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Argb32.PixelOperations.Generated.cs @@ -57,7 +57,7 @@ public partial struct Argb32 { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } - + /// public override void ToRgba32( Configuration configuration, @@ -71,7 +71,7 @@ public partial struct Argb32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToRgba32(source, dest); } - + /// public override void FromRgba32( Configuration configuration, @@ -84,8 +84,8 @@ public partial struct Argb32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToArgb32(source, dest); - } - + } + /// public override void ToAbgr32( Configuration configuration, @@ -99,7 +99,7 @@ public partial struct Argb32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToAbgr32(source, dest); } - + /// public override void FromAbgr32( Configuration configuration, @@ -112,8 +112,8 @@ public partial struct Argb32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToArgb32(source, dest); - } - + } + /// public override void ToBgra32( Configuration configuration, @@ -127,7 +127,7 @@ public partial struct Argb32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToBgra32(source, dest); } - + /// public override void FromBgra32( Configuration configuration, @@ -140,8 +140,8 @@ public partial struct Argb32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToArgb32(source, dest); - } - + } + /// public override void ToRgb24( Configuration configuration, @@ -155,7 +155,7 @@ public partial struct Argb32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToRgb24(source, dest); } - + /// public override void FromRgb24( Configuration configuration, @@ -168,8 +168,8 @@ public partial struct Argb32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToArgb32(source, dest); - } - + } + /// public override void ToBgr24( Configuration configuration, @@ -183,7 +183,7 @@ public partial struct Argb32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToBgr24(source, dest); } - + /// public override void FromBgr24( Configuration configuration, @@ -196,7 +196,7 @@ public partial struct Argb32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToArgb32(source, dest); - } + } /// public override void ToL8( @@ -210,7 +210,7 @@ public partial struct Argb32 ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -231,7 +231,7 @@ public partial struct Argb32 ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -252,7 +252,7 @@ public partial struct Argb32 ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -273,7 +273,7 @@ public partial struct Argb32 ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -294,7 +294,7 @@ public partial struct Argb32 ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -315,7 +315,7 @@ public partial struct Argb32 ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -336,7 +336,7 @@ public partial struct Argb32 ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs index dbadb86014..f604d6f970 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgr24.PixelOperations.Generated.cs @@ -57,7 +57,7 @@ public partial struct Bgr24 { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } - + /// public override void ToRgba32( Configuration configuration, @@ -71,7 +71,7 @@ public partial struct Bgr24 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToRgba32(source, dest); } - + /// public override void FromRgba32( Configuration configuration, @@ -84,8 +84,8 @@ public partial struct Bgr24 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToBgr24(source, dest); - } - + } + /// public override void ToArgb32( Configuration configuration, @@ -99,7 +99,7 @@ public partial struct Bgr24 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToArgb32(source, dest); } - + /// public override void FromArgb32( Configuration configuration, @@ -112,8 +112,8 @@ public partial struct Bgr24 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToBgr24(source, dest); - } - + } + /// public override void ToAbgr32( Configuration configuration, @@ -127,7 +127,7 @@ public partial struct Bgr24 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToAbgr32(source, dest); } - + /// public override void FromAbgr32( Configuration configuration, @@ -140,8 +140,8 @@ public partial struct Bgr24 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToBgr24(source, dest); - } - + } + /// public override void ToBgra32( Configuration configuration, @@ -155,7 +155,7 @@ public partial struct Bgr24 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToBgra32(source, dest); } - + /// public override void FromBgra32( Configuration configuration, @@ -168,8 +168,8 @@ public partial struct Bgr24 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToBgr24(source, dest); - } - + } + /// public override void ToRgb24( Configuration configuration, @@ -183,7 +183,7 @@ public partial struct Bgr24 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToRgb24(source, dest); } - + /// public override void FromRgb24( Configuration configuration, @@ -196,7 +196,7 @@ public partial struct Bgr24 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToBgr24(source, dest); - } + } /// public override void ToL8( @@ -210,7 +210,7 @@ public partial struct Bgr24 ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -231,7 +231,7 @@ public partial struct Bgr24 ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -252,7 +252,7 @@ public partial struct Bgr24 ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -273,7 +273,7 @@ public partial struct Bgr24 ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -294,7 +294,7 @@ public partial struct Bgr24 ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -315,7 +315,7 @@ public partial struct Bgr24 ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -336,7 +336,7 @@ public partial struct Bgr24 ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs index b3643313c4..b9f7a49e11 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra32.PixelOperations.Generated.cs @@ -57,7 +57,7 @@ public partial struct Bgra32 { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } - + /// public override void ToRgba32( Configuration configuration, @@ -71,7 +71,7 @@ public partial struct Bgra32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToRgba32(source, dest); } - + /// public override void FromRgba32( Configuration configuration, @@ -84,8 +84,8 @@ public partial struct Bgra32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToBgra32(source, dest); - } - + } + /// public override void ToArgb32( Configuration configuration, @@ -99,7 +99,7 @@ public partial struct Bgra32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToArgb32(source, dest); } - + /// public override void FromArgb32( Configuration configuration, @@ -112,8 +112,8 @@ public partial struct Bgra32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToBgra32(source, dest); - } - + } + /// public override void ToAbgr32( Configuration configuration, @@ -127,7 +127,7 @@ public partial struct Bgra32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToAbgr32(source, dest); } - + /// public override void FromAbgr32( Configuration configuration, @@ -140,8 +140,8 @@ public partial struct Bgra32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToBgra32(source, dest); - } - + } + /// public override void ToRgb24( Configuration configuration, @@ -155,7 +155,7 @@ public partial struct Bgra32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToRgb24(source, dest); } - + /// public override void FromRgb24( Configuration configuration, @@ -168,8 +168,8 @@ public partial struct Bgra32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToBgra32(source, dest); - } - + } + /// public override void ToBgr24( Configuration configuration, @@ -183,7 +183,7 @@ public partial struct Bgra32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToBgr24(source, dest); } - + /// public override void FromBgr24( Configuration configuration, @@ -196,7 +196,7 @@ public partial struct Bgra32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToBgra32(source, dest); - } + } /// public override void ToL8( @@ -210,7 +210,7 @@ public partial struct Bgra32 ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -231,7 +231,7 @@ public partial struct Bgra32 ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -252,7 +252,7 @@ public partial struct Bgra32 ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -273,7 +273,7 @@ public partial struct Bgra32 ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -294,7 +294,7 @@ public partial struct Bgra32 ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -315,7 +315,7 @@ public partial struct Bgra32 ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -336,7 +336,7 @@ public partial struct Bgra32 ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra5551.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra5551.PixelOperations.Generated.cs index 52022b0c78..c1ba4f0618 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra5551.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Bgra5551.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ public partial struct Bgra5551 ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -71,7 +71,7 @@ public partial struct Bgra5551 ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); @@ -92,7 +92,7 @@ public partial struct Bgra5551 ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -113,7 +113,7 @@ public partial struct Bgra5551 ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -134,7 +134,7 @@ public partial struct Bgra5551 ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -155,7 +155,7 @@ public partial struct Bgra5551 ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -176,7 +176,7 @@ public partial struct Bgra5551 ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -197,7 +197,7 @@ public partial struct Bgra5551 ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -218,7 +218,7 @@ public partial struct Bgra5551 ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -239,7 +239,7 @@ public partial struct Bgra5551 ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -260,7 +260,7 @@ public partial struct Bgra5551 ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -281,7 +281,7 @@ public partial struct Bgra5551 ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L16.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L16.PixelOperations.Generated.cs index a1a59727ff..c38d752ea6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L16.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L16.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ public partial struct L16 ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -71,7 +71,7 @@ public partial struct L16 ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); @@ -92,7 +92,7 @@ public partial struct L16 ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -113,7 +113,7 @@ public partial struct L16 ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -134,7 +134,7 @@ public partial struct L16 ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -155,7 +155,7 @@ public partial struct L16 ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -176,7 +176,7 @@ public partial struct L16 ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -197,7 +197,7 @@ public partial struct L16 ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -218,7 +218,7 @@ public partial struct L16 ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -239,7 +239,7 @@ public partial struct L16 ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -260,7 +260,7 @@ public partial struct L16 ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -281,7 +281,7 @@ public partial struct L16 ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L8.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L8.PixelOperations.Generated.cs index f0ada6459c..656a0546ba 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L8.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/L8.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ public partial struct L8 ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -71,7 +71,7 @@ public partial struct L8 ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); @@ -92,7 +92,7 @@ public partial struct L8 ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -113,7 +113,7 @@ public partial struct L8 ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -134,7 +134,7 @@ public partial struct L8 ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -155,7 +155,7 @@ public partial struct L8 ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -176,7 +176,7 @@ public partial struct L8 ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -197,7 +197,7 @@ public partial struct L8 ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -218,7 +218,7 @@ public partial struct L8 ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -239,7 +239,7 @@ public partial struct L8 ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -260,7 +260,7 @@ public partial struct L8 ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -281,7 +281,7 @@ public partial struct L8 ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La16.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La16.PixelOperations.Generated.cs index d0610037d5..82be1323cd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La16.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La16.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ public partial struct La16 ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -71,7 +71,7 @@ public partial struct La16 ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); @@ -92,7 +92,7 @@ public partial struct La16 ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -113,7 +113,7 @@ public partial struct La16 ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -134,7 +134,7 @@ public partial struct La16 ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -155,7 +155,7 @@ public partial struct La16 ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -176,7 +176,7 @@ public partial struct La16 ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -197,7 +197,7 @@ public partial struct La16 ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -218,7 +218,7 @@ public partial struct La16 ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -239,7 +239,7 @@ public partial struct La16 ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -260,7 +260,7 @@ public partial struct La16 ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -281,7 +281,7 @@ public partial struct La16 ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La32.PixelOperations.Generated.cs index 76da66a1ad..a9ee9d6b78 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/La32.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ public partial struct La32 ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -71,7 +71,7 @@ public partial struct La32 ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); @@ -92,7 +92,7 @@ public partial struct La32 ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -113,7 +113,7 @@ public partial struct La32 ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -134,7 +134,7 @@ public partial struct La32 ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -155,7 +155,7 @@ public partial struct La32 ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -176,7 +176,7 @@ public partial struct La32 ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -197,7 +197,7 @@ public partial struct La32 ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -218,7 +218,7 @@ public partial struct La32 ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -239,7 +239,7 @@ public partial struct La32 ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -260,7 +260,7 @@ public partial struct La32 ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -281,7 +281,7 @@ public partial struct La32 ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs index fd1cfd74de..1d87121e92 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb24.PixelOperations.Generated.cs @@ -57,7 +57,7 @@ public partial struct Rgb24 { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } - + /// public override void ToRgba32( Configuration configuration, @@ -71,7 +71,7 @@ public partial struct Rgb24 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToRgba32(source, dest); } - + /// public override void FromRgba32( Configuration configuration, @@ -84,8 +84,8 @@ public partial struct Rgb24 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToRgb24(source, dest); - } - + } + /// public override void ToArgb32( Configuration configuration, @@ -99,7 +99,7 @@ public partial struct Rgb24 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToArgb32(source, dest); } - + /// public override void FromArgb32( Configuration configuration, @@ -112,8 +112,8 @@ public partial struct Rgb24 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToRgb24(source, dest); - } - + } + /// public override void ToAbgr32( Configuration configuration, @@ -127,7 +127,7 @@ public partial struct Rgb24 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToAbgr32(source, dest); } - + /// public override void FromAbgr32( Configuration configuration, @@ -140,8 +140,8 @@ public partial struct Rgb24 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToRgb24(source, dest); - } - + } + /// public override void ToBgra32( Configuration configuration, @@ -155,7 +155,7 @@ public partial struct Rgb24 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToBgra32(source, dest); } - + /// public override void FromBgra32( Configuration configuration, @@ -168,8 +168,8 @@ public partial struct Rgb24 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToRgb24(source, dest); - } - + } + /// public override void ToBgr24( Configuration configuration, @@ -183,7 +183,7 @@ public partial struct Rgb24 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToBgr24(source, dest); } - + /// public override void FromBgr24( Configuration configuration, @@ -196,7 +196,7 @@ public partial struct Rgb24 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToRgb24(source, dest); - } + } /// public override void ToL8( @@ -210,7 +210,7 @@ public partial struct Rgb24 ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -231,7 +231,7 @@ public partial struct Rgb24 ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -252,7 +252,7 @@ public partial struct Rgb24 ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -273,7 +273,7 @@ public partial struct Rgb24 ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -294,7 +294,7 @@ public partial struct Rgb24 ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -315,7 +315,7 @@ public partial struct Rgb24 ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -336,7 +336,7 @@ public partial struct Rgb24 ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb48.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb48.PixelOperations.Generated.cs index 417ed34334..60cfdad4b6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb48.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgb48.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ public partial struct Rgb48 ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -71,7 +71,7 @@ public partial struct Rgb48 ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); @@ -92,7 +92,7 @@ public partial struct Rgb48 ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -113,7 +113,7 @@ public partial struct Rgb48 ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -134,7 +134,7 @@ public partial struct Rgb48 ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -155,7 +155,7 @@ public partial struct Rgb48 ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -176,7 +176,7 @@ public partial struct Rgb48 ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -197,7 +197,7 @@ public partial struct Rgb48 ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -218,7 +218,7 @@ public partial struct Rgb48 ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -239,7 +239,7 @@ public partial struct Rgb48 ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -260,7 +260,7 @@ public partial struct Rgb48 ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -281,7 +281,7 @@ public partial struct Rgb48 ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba32.PixelOperations.Generated.cs index c635a4d6a4..da7c9a6c91 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba32.PixelOperations.Generated.cs @@ -37,7 +37,7 @@ public partial struct Rgba32 sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } - + /// public override void ToArgb32( Configuration configuration, @@ -51,7 +51,7 @@ public partial struct Rgba32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToArgb32(source, dest); } - + /// public override void FromArgb32( Configuration configuration, @@ -64,8 +64,8 @@ public partial struct Rgba32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToRgba32(source, dest); - } - + } + /// public override void ToAbgr32( Configuration configuration, @@ -79,7 +79,7 @@ public partial struct Rgba32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToAbgr32(source, dest); } - + /// public override void FromAbgr32( Configuration configuration, @@ -92,8 +92,8 @@ public partial struct Rgba32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToRgba32(source, dest); - } - + } + /// public override void ToBgra32( Configuration configuration, @@ -107,7 +107,7 @@ public partial struct Rgba32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToBgra32(source, dest); } - + /// public override void FromBgra32( Configuration configuration, @@ -120,8 +120,8 @@ public partial struct Rgba32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToRgba32(source, dest); - } - + } + /// public override void ToRgb24( Configuration configuration, @@ -135,7 +135,7 @@ public partial struct Rgba32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToRgb24(source, dest); } - + /// public override void FromRgb24( Configuration configuration, @@ -148,8 +148,8 @@ public partial struct Rgba32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToRgba32(source, dest); - } - + } + /// public override void ToBgr24( Configuration configuration, @@ -163,7 +163,7 @@ public partial struct Rgba32 Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToBgr24(source, dest); } - + /// public override void FromBgr24( Configuration configuration, @@ -176,7 +176,7 @@ public partial struct Rgba32 ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToRgba32(source, dest); - } + } /// public override void ToL8( @@ -190,7 +190,7 @@ public partial struct Rgba32 ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -211,7 +211,7 @@ public partial struct Rgba32 ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -232,7 +232,7 @@ public partial struct Rgba32 ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -253,7 +253,7 @@ public partial struct Rgba32 ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -274,7 +274,7 @@ public partial struct Rgba32 ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -295,7 +295,7 @@ public partial struct Rgba32 ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); @@ -316,7 +316,7 @@ public partial struct Rgba32 ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba64.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba64.PixelOperations.Generated.cs index 395f7e88e0..b6236f8a66 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba64.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/Rgba64.PixelOperations.Generated.cs @@ -50,7 +50,7 @@ public partial struct Rgba64 ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); @@ -71,7 +71,7 @@ public partial struct Rgba64 ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Abgr32 dp = ref Unsafe.Add(ref destRef, i); @@ -92,7 +92,7 @@ public partial struct Rgba64 ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); @@ -113,7 +113,7 @@ public partial struct Rgba64 ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); @@ -134,7 +134,7 @@ public partial struct Rgba64 ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref L8 dp = ref Unsafe.Add(ref destRef, i); @@ -155,7 +155,7 @@ public partial struct Rgba64 ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref L16 dp = ref Unsafe.Add(ref destRef, i); @@ -176,7 +176,7 @@ public partial struct Rgba64 ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref La16 dp = ref Unsafe.Add(ref destRef, i); @@ -197,7 +197,7 @@ public partial struct Rgba64 ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref La32 dp = ref Unsafe.Add(ref destRef, i); @@ -218,7 +218,7 @@ public partial struct Rgba64 ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); @@ -239,7 +239,7 @@ public partial struct Rgba64 ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); @@ -260,7 +260,7 @@ public partial struct Rgba64 ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); @@ -281,7 +281,7 @@ public partial struct Rgba64 ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude index ab08bfa570..f7e178d7f2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/Generated/_Common.ttinclude @@ -103,7 +103,7 @@ using SixLabors.ImageSharp.PixelFormats.Utils; ref <#=fromPixelType#> sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref <#=toPixelType#> destRef = ref MemoryMarshal.GetReference(destinationPixels); - for (nint i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref <#=fromPixelType#> sp = ref Unsafe.Add(ref sourceRef, i); ref <#=toPixelType#> dp = ref Unsafe.Add(ref destRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs index 3fcb4bc610..a3833583fc 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs @@ -72,7 +72,7 @@ public partial struct RgbaVector ref Vector4 sourceBaseRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); ref L8 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Vector4 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref L8 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -91,7 +91,7 @@ public partial struct RgbaVector ref Vector4 sourceBaseRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); ref L16 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref Vector4 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref L16 dp = ref Unsafe.Add(ref destBaseRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs index a4c2fb7a0c..7e84bd6392 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs @@ -22,7 +22,7 @@ public partial class PixelOperations ref Argb32 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -58,7 +58,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref Argb32 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -94,7 +94,7 @@ public partial class PixelOperations ref Abgr32 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref Abgr32 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -130,7 +130,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref Abgr32 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -166,7 +166,7 @@ public partial class PixelOperations ref Bgr24 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -202,7 +202,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -238,7 +238,7 @@ public partial class PixelOperations ref Bgra32 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -274,7 +274,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -310,7 +310,7 @@ public partial class PixelOperations ref L8 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref L8 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -346,7 +346,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref L8 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref L8 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -382,7 +382,7 @@ public partial class PixelOperations ref L16 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref L16 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -418,7 +418,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref L16 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref L16 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -454,7 +454,7 @@ public partial class PixelOperations ref La16 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref La16 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -490,7 +490,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref La16 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref La16 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -526,7 +526,7 @@ public partial class PixelOperations ref La32 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref La32 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -562,7 +562,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref La32 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref La32 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -598,7 +598,7 @@ public partial class PixelOperations ref Rgb24 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -634,7 +634,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -670,7 +670,7 @@ public partial class PixelOperations ref Rgba32 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -706,7 +706,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -742,7 +742,7 @@ public partial class PixelOperations ref Rgb48 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -778,7 +778,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -814,7 +814,7 @@ public partial class PixelOperations ref Rgba64 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -850,7 +850,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destBaseRef, i); @@ -886,7 +886,7 @@ public partial class PixelOperations ref Bgra5551 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref Bgra5551 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -922,7 +922,7 @@ public partial class PixelOperations ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref Bgra5551 dp = ref Unsafe.Add(ref destBaseRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt index 31bd0ce6d0..8ba3398e39 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt @@ -28,7 +28,7 @@ ref <#=pixelType#> sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref <#=pixelType#> sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); @@ -70,7 +70,7 @@ ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); ref <#=pixelType#> destBaseRef = ref MemoryMarshal.GetReference(destinationPixels); - for (int i = 0; i < sourcePixels.Length; i++) + for (nuint i = 0; i < (uint)sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); ref <#=pixelType#> dp = ref Unsafe.Add(ref destBaseRef, i); diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 2465493f58..9d93d27aca 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -187,7 +187,7 @@ public partial class PixelOperations ref byte b = ref MemoryMarshal.GetReference(blueChannel); ref TPixel d = ref MemoryMarshal.GetReference(destination); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { rgb24.R = Unsafe.Add(ref r, i); rgb24.G = Unsafe.Add(ref g, i); @@ -218,7 +218,7 @@ public partial class PixelOperations ref float g = ref MemoryMarshal.GetReference(greenChannel); ref float b = ref MemoryMarshal.GetReference(blueChannel); ref TPixel src = ref MemoryMarshal.GetReference(source); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { Unsafe.Add(ref src, i).ToRgba32(ref rgba32); Unsafe.Add(ref r, i) = rgba32.R; diff --git a/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs b/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs index 3ae5a3b5e4..50a68906e7 100644 --- a/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs +++ b/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs @@ -29,6 +29,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToArgb32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); @@ -38,6 +40,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgra32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); @@ -47,6 +51,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToAbgr32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); @@ -56,6 +62,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgb24(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4Slice3(source, dest, default); @@ -65,9 +73,11 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgr24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(3, 0, 1, 2)); + => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(SimdUtils.Shuffle.MMShuffle3012)); } /// @@ -82,6 +92,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgba32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); @@ -91,6 +103,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgra32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); @@ -100,6 +114,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToAbgr32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); @@ -109,18 +125,22 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgb24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(0, 3, 2, 1)); + => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(SimdUtils.Shuffle.MMShuffle0321)); /// /// Converts a representing a collection of /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgr24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(0, 1, 2, 3)); + => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(SimdUtils.Shuffle.MMShuffle0123)); } /// @@ -135,6 +155,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToArgb32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); @@ -144,6 +166,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgba32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); @@ -153,6 +177,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToAbgr32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); @@ -162,15 +188,19 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgb24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(3, 0, 1, 2)); + => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(SimdUtils.Shuffle.MMShuffle3012)); /// /// Converts a representing a collection of /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgr24(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4Slice3(source, dest, default); @@ -188,6 +218,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToArgb32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); @@ -197,6 +229,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgba32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); @@ -206,6 +240,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgra32(ReadOnlySpan source, Span dest) => SimdUtils.Shuffle4(source, dest, default); @@ -215,18 +251,22 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgb24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(0, 1, 2, 3)); + => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(SimdUtils.Shuffle.MMShuffle0123)); /// /// Converts a representing a collection of /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgr24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(0, 3, 2, 1)); + => SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(SimdUtils.Shuffle.MMShuffle0321)); } /// @@ -241,6 +281,8 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgba32(ReadOnlySpan source, Span dest) => SimdUtils.Pad3Shuffle4(source, dest, default); @@ -250,36 +292,44 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToArgb32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(2, 1, 0, 3)); + => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(SimdUtils.Shuffle.MMShuffle2103)); /// /// Converts a representing a collection of /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgra32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(3, 0, 1, 2)); + => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(SimdUtils.Shuffle.MMShuffle3012)); /// /// Converts a representing a collection of /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToAbgr32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(0, 1, 2, 3)); + => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(SimdUtils.Shuffle.MMShuffle0123)); /// /// Converts a representing a collection of /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgr24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle3(source, dest, new DefaultShuffle3(0, 1, 2)); + => SimdUtils.Shuffle3(source, dest, new DefaultShuffle3(SimdUtils.Shuffle.MMShuffle3012)); } /// @@ -294,24 +344,30 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToArgb32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(0, 1, 2, 3)); + => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(SimdUtils.Shuffle.MMShuffle0123)); /// /// Converts a representing a collection of /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgba32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(3, 0, 1, 2)); + => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(SimdUtils.Shuffle.MMShuffle3012)); /// /// Converts a representing a collection of /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToBgra32(ReadOnlySpan source, Span dest) => SimdUtils.Pad3Shuffle4(source, dest, default); @@ -321,17 +377,21 @@ internal static class PixelConverter /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToAbgr32(ReadOnlySpan source, Span dest) - => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(2, 1, 0, 3)); + => SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(SimdUtils.Shuffle.MMShuffle2103)); /// /// Converts a representing a collection of /// pixels to a representing /// a collection of pixels. /// + /// The source span of bytes. + /// The destination span of bytes. [MethodImpl(InliningOptions.ShortMethod)] public static void ToRgb24(ReadOnlySpan source, Span dest) - => SimdUtils.Shuffle3(source, dest, new DefaultShuffle3(0, 1, 2)); + => SimdUtils.Shuffle3(source, dest, new DefaultShuffle3(SimdUtils.Shuffle.MMShuffle3012)); } } diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs index 251f8d64db..4d07a8a9b6 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Numerics; @@ -88,7 +88,7 @@ internal static partial class Vector4Converters where TPixel : unmanaged, IPixel { ref Vector4 sourceStart = ref MemoryMarshal.GetReference(sourceVectors); - ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceStart, sourceVectors.Length); + ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceStart, (uint)sourceVectors.Length); ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) @@ -107,7 +107,7 @@ internal static partial class Vector4Converters where TPixel : unmanaged, IPixel { ref TPixel sourceStart = ref MemoryMarshal.GetReference(sourcePixels); - ref TPixel sourceEnd = ref Unsafe.Add(ref sourceStart, sourcePixels.Length); + ref TPixel sourceEnd = ref Unsafe.Add(ref sourceStart, (uint)sourcePixels.Length); ref Vector4 destRef = ref MemoryMarshal.GetReference(destVectors); while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) @@ -126,7 +126,7 @@ internal static partial class Vector4Converters where TPixel : unmanaged, IPixel { ref Vector4 sourceStart = ref MemoryMarshal.GetReference(sourceVectors); - ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceStart, sourceVectors.Length); + ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceStart, (uint)sourceVectors.Length); ref TPixel destRef = ref MemoryMarshal.GetReference(destinationColors); while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) @@ -145,7 +145,7 @@ internal static partial class Vector4Converters where TPixel : unmanaged, IPixel { ref TPixel sourceStart = ref MemoryMarshal.GetReference(sourceColors); - ref TPixel sourceEnd = ref Unsafe.Add(ref sourceStart, sourceColors.Length); + ref TPixel sourceEnd = ref Unsafe.Add(ref sourceStart, (uint)sourceColors.Length); ref Vector4 destRef = ref MemoryMarshal.GetReference(destinationVectors); while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) diff --git a/src/ImageSharp/Primitives/ColorMatrix.Impl.cs b/src/ImageSharp/Primitives/ColorMatrix.Impl.cs new file mode 100644 index 0000000000..559fcdde08 --- /dev/null +++ b/src/ImageSharp/Primitives/ColorMatrix.Impl.cs @@ -0,0 +1,208 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +#pragma warning disable SA1117 // Parameters should be on same line or separate lines +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp; + +/// +/// A structure encapsulating a 5x4 matrix used for transforming the color and alpha components of an image. +/// +public partial struct ColorMatrix +{ + [UnscopedRef] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ref Impl AsImpl() => ref Unsafe.As(ref this); + + [UnscopedRef] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal readonly ref readonly Impl AsROImpl() => ref Unsafe.As(ref Unsafe.AsRef(in this)); + + internal struct Impl : IEquatable + { + public Vector4 X; + public Vector4 Y; + public Vector4 Z; + public Vector4 W; + public Vector4 V; + + public static Impl Identity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Impl result; + + result.X = Vector4.UnitX; + result.Y = Vector4.UnitY; + result.Z = Vector4.UnitZ; + result.W = Vector4.UnitW; + result.V = Vector4.Zero; + + return result; + } + } + + public readonly bool IsIdentity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => + (this.X == Vector4.UnitX) + && (this.Y == Vector4.UnitY) + && (this.Z == Vector4.UnitZ) + && (this.W == Vector4.UnitW) + && (this.V == Vector4.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Impl operator +(in Impl left, in Impl right) + { + Impl result; + + result.X = left.X + right.X; + result.Y = left.Y + right.Y; + result.Z = left.Z + right.Z; + result.W = left.W + right.W; + result.V = left.V + right.V; + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Impl operator -(in Impl left, in Impl right) + { + Impl result; + + result.X = left.X - right.X; + result.Y = left.Y - right.Y; + result.Z = left.Z - right.Z; + result.W = left.W - right.W; + result.V = left.V - right.V; + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Impl operator -(in Impl value) + { + Impl result; + + result.X = -value.X; + result.Y = -value.Y; + result.Z = -value.Z; + result.W = -value.W; + result.V = -value.V; + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Impl operator *(in Impl left, in Impl right) + { + Impl result; + + // result.X = Transform(left.X, in right); + result.X = right.X * left.X.X; + result.X += right.Y * left.X.Y; + result.X += right.Z * left.X.Z; + result.X += right.W * left.X.W; + + // result.Y = Transform(left.Y, in right); + result.Y = right.X * left.Y.X; + result.Y += right.Y * left.Y.Y; + result.Y += right.Z * left.Y.Z; + result.Y += right.W * left.Y.W; + + // result.Z = Transform(left.Z, in right); + result.Z = right.X * left.Z.X; + result.Z += right.Y * left.Z.Y; + result.Z += right.Z * left.Z.Z; + result.Z += right.W * left.Z.W; + + // result.W = Transform(left.W, in right); + result.W = right.X * left.W.X; + result.W += right.Y * left.W.Y; + result.W += right.Z * left.W.Z; + result.W += right.W * left.W.W; + + // result.V = Transform(left.V, in right); + result.V = right.X * left.V.X; + result.V += right.Y * left.V.Y; + result.V += right.Z * left.V.Z; + result.V += right.W * left.V.W; + + result.V += right.V; + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Impl operator *(in Impl left, float right) + { + Impl result; + + result.X = left.X * right; + result.Y = left.Y * right; + result.Z = left.Z * right; + result.W = left.W * right; + result.V = left.V * right; + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(in Impl left, in Impl right) => + (left.X == right.X) + && (left.Y == right.Y) + && (left.Z == right.Z) + && (left.W == right.W) + && (left.V == right.V); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(in Impl left, in Impl right) => + (left.X != right.X) + && (left.Y != right.Y) + && (left.Z != right.Z) + && (left.W != right.W) + && (left.V != right.V); + + [UnscopedRef] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref ColorMatrix AsColorMatrix() => ref Unsafe.As(ref this); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Init( + float m11, float m12, float m13, float m14, + float m21, float m22, float m23, float m24, + float m31, float m32, float m33, float m34, + float m41, float m42, float m43, float m44, + float m51, float m52, float m53, float m54) + { + this.X = new Vector4(m11, m12, m13, m14); + this.Y = new Vector4(m21, m22, m23, m24); + this.Z = new Vector4(m31, m32, m33, m34); + this.W = new Vector4(m41, m42, m43, m44); + this.V = new Vector4(m51, m52, m53, m54); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly bool Equals([NotNullWhen(true)] object? obj) + => (obj is ColorMatrix other) && this.Equals(in other.AsImpl()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(in Impl other) => + this.X.Equals(other.X) + && this.Y.Equals(other.Y) + && this.Z.Equals(other.Z) + && this.W.Equals(other.W) + && this.V.Equals(other.V); + + bool IEquatable.Equals(Impl other) => this.Equals(in other); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z, this.W, this.V); + } +} diff --git a/src/ImageSharp/Primitives/ColorMatrix.cs b/src/ImageSharp/Primitives/ColorMatrix.cs index e06dc1f6a5..9f8b09e4b5 100644 --- a/src/ImageSharp/Primitives/ColorMatrix.cs +++ b/src/ImageSharp/Primitives/ColorMatrix.cs @@ -2,7 +2,10 @@ // Licensed under the Six Labors Split License. #pragma warning disable SA1117 // Parameters should be on same line or separate lines + +using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace SixLabors.ImageSharp; @@ -11,7 +14,7 @@ namespace SixLabors.ImageSharp; /// A structure encapsulating a 5x4 matrix used for transforming the color and alpha components of an image. /// [StructLayout(LayoutKind.Sequential)] -public struct ColorMatrix : IEquatable +public partial struct ColorMatrix : IEquatable { /// /// Value at row 1, column 1 of the matrix. @@ -137,62 +140,33 @@ public struct ColorMatrix : IEquatable /// The value at row 5, column 3 of the matrix. /// The value at row 5, column 4 of the matrix. public ColorMatrix(float m11, float m12, float m13, float m14, - float m21, float m22, float m23, float m24, - float m31, float m32, float m33, float m34, - float m41, float m42, float m43, float m44, - float m51, float m52, float m53, float m54) + float m21, float m22, float m23, float m24, + float m31, float m32, float m33, float m34, + float m41, float m42, float m43, float m44, + float m51, float m52, float m53, float m54) { - this.M11 = m11; - this.M12 = m12; - this.M13 = m13; - this.M14 = m14; - - this.M21 = m21; - this.M22 = m22; - this.M23 = m23; - this.M24 = m24; - - this.M31 = m31; - this.M32 = m32; - this.M33 = m33; - this.M34 = m34; - - this.M41 = m41; - this.M42 = m42; - this.M43 = m43; - this.M44 = m44; - - this.M51 = m51; - this.M52 = m52; - this.M53 = m53; - this.M54 = m54; + Unsafe.SkipInit(out this); + + this.AsImpl().Init(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44, m51, m52, m53, + m54); } /// /// Gets the multiplicative identity matrix. /// - public static ColorMatrix Identity { get; } = - new ColorMatrix(1F, 0F, 0F, 0F, - 0F, 1F, 0F, 0F, - 0F, 0F, 1F, 0F, - 0F, 0F, 0F, 1F, - 0F, 0F, 0F, 0F); + public static ColorMatrix Identity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Impl.Identity.AsColorMatrix(); + } /// /// Gets a value indicating whether the matrix is the identity matrix. /// public bool IsIdentity { - get - { - // Check diagonal element first for early out. - return this.M11 == 1F && this.M22 == 1F && this.M33 == 1F && this.M44 == 1F - && this.M12 == 0F && this.M13 == 0F && this.M14 == 0F - && this.M21 == 0F && this.M23 == 0F && this.M24 == 0F - && this.M31 == 0F && this.M32 == 0F && this.M34 == 0F - && this.M41 == 0F && this.M42 == 0F && this.M43 == 0F - && this.M51 == 0F && this.M52 == 0F && this.M53 == 0F && this.M54 == 0F; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.AsROImpl().IsIdentity; } /// @@ -202,32 +176,7 @@ public struct ColorMatrix : IEquatable /// The second source matrix. /// The resulting matrix. public static ColorMatrix operator +(ColorMatrix value1, ColorMatrix value2) - { - var m = default(ColorMatrix); - - m.M11 = value1.M11 + value2.M11; - m.M12 = value1.M12 + value2.M12; - m.M13 = value1.M13 + value2.M13; - m.M14 = value1.M14 + value2.M14; - m.M21 = value1.M21 + value2.M21; - m.M22 = value1.M22 + value2.M22; - m.M23 = value1.M23 + value2.M23; - m.M24 = value1.M24 + value2.M24; - m.M31 = value1.M31 + value2.M31; - m.M32 = value1.M32 + value2.M32; - m.M33 = value1.M33 + value2.M33; - m.M34 = value1.M34 + value2.M34; - m.M41 = value1.M41 + value2.M41; - m.M42 = value1.M42 + value2.M42; - m.M43 = value1.M43 + value2.M43; - m.M44 = value1.M44 + value2.M44; - m.M51 = value1.M51 + value2.M51; - m.M52 = value1.M52 + value2.M52; - m.M53 = value1.M53 + value2.M53; - m.M54 = value1.M54 + value2.M54; - - return m; - } + => (value1.AsImpl() + value2.AsImpl()).AsColorMatrix(); /// /// Subtracts the second matrix from the first. @@ -236,32 +185,7 @@ public struct ColorMatrix : IEquatable /// The second source matrix. /// The result of the subtraction. public static ColorMatrix operator -(ColorMatrix value1, ColorMatrix value2) - { - var m = default(ColorMatrix); - - m.M11 = value1.M11 - value2.M11; - m.M12 = value1.M12 - value2.M12; - m.M13 = value1.M13 - value2.M13; - m.M14 = value1.M14 - value2.M14; - m.M21 = value1.M21 - value2.M21; - m.M22 = value1.M22 - value2.M22; - m.M23 = value1.M23 - value2.M23; - m.M24 = value1.M24 - value2.M24; - m.M31 = value1.M31 - value2.M31; - m.M32 = value1.M32 - value2.M32; - m.M33 = value1.M33 - value2.M33; - m.M34 = value1.M34 - value2.M34; - m.M41 = value1.M41 - value2.M41; - m.M42 = value1.M42 - value2.M42; - m.M43 = value1.M43 - value2.M43; - m.M44 = value1.M44 - value2.M44; - m.M51 = value1.M51 - value2.M51; - m.M52 = value1.M52 - value2.M52; - m.M53 = value1.M53 - value2.M53; - m.M54 = value1.M54 - value2.M54; - - return m; - } + => (value1.AsImpl() - value2.AsImpl()).AsColorMatrix(); /// /// Returns a new matrix with the negated elements of the given matrix. @@ -269,32 +193,7 @@ public struct ColorMatrix : IEquatable /// The source matrix. /// The negated matrix. public static ColorMatrix operator -(ColorMatrix value) - { - var m = default(ColorMatrix); - - m.M11 = -value.M11; - m.M12 = -value.M12; - m.M13 = -value.M13; - m.M14 = -value.M14; - m.M21 = -value.M21; - m.M22 = -value.M22; - m.M23 = -value.M23; - m.M24 = -value.M24; - m.M31 = -value.M31; - m.M32 = -value.M32; - m.M33 = -value.M33; - m.M34 = -value.M34; - m.M41 = -value.M41; - m.M42 = -value.M42; - m.M43 = -value.M43; - m.M44 = -value.M44; - m.M51 = -value.M51; - m.M52 = -value.M52; - m.M53 = -value.M53; - m.M54 = -value.M54; - - return m; - } + => (-value.AsImpl()).AsColorMatrix(); /// /// Multiplies a matrix by another matrix. @@ -303,41 +202,7 @@ public struct ColorMatrix : IEquatable /// The second source matrix. /// The result of the multiplication. public static ColorMatrix operator *(ColorMatrix value1, ColorMatrix value2) - { - var m = default(ColorMatrix); - - // First row - m.M11 = (value1.M11 * value2.M11) + (value1.M12 * value2.M21) + (value1.M13 * value2.M31) + (value1.M14 * value2.M41); - m.M12 = (value1.M11 * value2.M12) + (value1.M12 * value2.M22) + (value1.M13 * value2.M32) + (value1.M14 * value2.M42); - m.M13 = (value1.M11 * value2.M13) + (value1.M12 * value2.M23) + (value1.M13 * value2.M33) + (value1.M14 * value2.M43); - m.M14 = (value1.M11 * value2.M14) + (value1.M12 * value2.M24) + (value1.M13 * value2.M34) + (value1.M14 * value2.M44); - - // Second row - m.M21 = (value1.M21 * value2.M11) + (value1.M22 * value2.M21) + (value1.M23 * value2.M31) + (value1.M24 * value2.M41); - m.M22 = (value1.M21 * value2.M12) + (value1.M22 * value2.M22) + (value1.M23 * value2.M32) + (value1.M24 * value2.M42); - m.M23 = (value1.M21 * value2.M13) + (value1.M22 * value2.M23) + (value1.M23 * value2.M33) + (value1.M24 * value2.M43); - m.M24 = (value1.M21 * value2.M14) + (value1.M22 * value2.M24) + (value1.M23 * value2.M34) + (value1.M24 * value2.M44); - - // Third row - m.M31 = (value1.M31 * value2.M11) + (value1.M32 * value2.M21) + (value1.M33 * value2.M31) + (value1.M34 * value2.M41); - m.M32 = (value1.M31 * value2.M12) + (value1.M32 * value2.M22) + (value1.M33 * value2.M32) + (value1.M34 * value2.M42); - m.M33 = (value1.M31 * value2.M13) + (value1.M32 * value2.M23) + (value1.M33 * value2.M33) + (value1.M34 * value2.M43); - m.M34 = (value1.M31 * value2.M14) + (value1.M32 * value2.M24) + (value1.M33 * value2.M34) + (value1.M34 * value2.M44); - - // Fourth row - m.M41 = (value1.M41 * value2.M11) + (value1.M42 * value2.M21) + (value1.M43 * value2.M31) + (value1.M44 * value2.M41); - m.M42 = (value1.M41 * value2.M12) + (value1.M42 * value2.M22) + (value1.M43 * value2.M32) + (value1.M44 * value2.M42); - m.M43 = (value1.M41 * value2.M13) + (value1.M42 * value2.M23) + (value1.M43 * value2.M33) + (value1.M44 * value2.M43); - m.M44 = (value1.M41 * value2.M14) + (value1.M42 * value2.M24) + (value1.M43 * value2.M34) + (value1.M44 * value2.M44); - - // Fifth row - m.M51 = (value1.M51 * value2.M11) + (value1.M52 * value2.M21) + (value1.M53 * value2.M31) + (value1.M54 * value2.M41) + value2.M51; - m.M52 = (value1.M51 * value2.M12) + (value1.M52 * value2.M22) + (value1.M53 * value2.M32) + (value1.M54 * value2.M52) + value2.M52; - m.M53 = (value1.M51 * value2.M13) + (value1.M52 * value2.M23) + (value1.M53 * value2.M33) + (value1.M54 * value2.M53) + value2.M53; - m.M54 = (value1.M51 * value2.M14) + (value1.M52 * value2.M24) + (value1.M53 * value2.M34) + (value1.M54 * value2.M54) + value2.M54; - - return m; - } + => (value1.AsImpl() * value2.AsImpl()).AsColorMatrix(); /// /// Multiplies a matrix by a scalar value. @@ -346,32 +211,7 @@ public struct ColorMatrix : IEquatable /// The scaling factor. /// The scaled matrix. public static ColorMatrix operator *(ColorMatrix value1, float value2) - { - var m = default(ColorMatrix); - - m.M11 = value1.M11 * value2; - m.M12 = value1.M12 * value2; - m.M13 = value1.M13 * value2; - m.M14 = value1.M14 * value2; - m.M21 = value1.M21 * value2; - m.M22 = value1.M22 * value2; - m.M23 = value1.M23 * value2; - m.M24 = value1.M24 * value2; - m.M31 = value1.M31 * value2; - m.M32 = value1.M32 * value2; - m.M33 = value1.M33 * value2; - m.M34 = value1.M34 * value2; - m.M41 = value1.M41 * value2; - m.M42 = value1.M42 * value2; - m.M43 = value1.M43 * value2; - m.M44 = value1.M44 * value2; - m.M51 = value1.M51 * value2; - m.M52 = value1.M52 * value2; - m.M53 = value1.M53 * value2; - m.M54 = value1.M54 * value2; - - return m; - } + => (value1.AsImpl() * value2).AsColorMatrix(); /// /// Returns a boolean indicating whether the given two matrices are equal. @@ -379,7 +219,8 @@ public struct ColorMatrix : IEquatable /// The first matrix to compare. /// The second matrix to compare. /// True if the given matrices are equal; False otherwise. - public static bool operator ==(ColorMatrix value1, ColorMatrix value2) => value1.Equals(value2); + public static bool operator ==(ColorMatrix value1, ColorMatrix value2) + => value1.AsImpl() == value2.AsImpl(); /// /// Returns a boolean indicating whether the given two matrices are not equal. @@ -387,71 +228,35 @@ public struct ColorMatrix : IEquatable /// The first matrix to compare. /// The second matrix to compare. /// True if the given matrices are equal; False otherwise. - public static bool operator !=(ColorMatrix value1, ColorMatrix value2) => !value1.Equals(value2); + public static bool operator !=(ColorMatrix value1, ColorMatrix value2) + => value1.AsImpl() != value2.AsImpl(); /// - public override bool Equals(object? obj) => obj is ColorMatrix matrix && this.Equals(matrix); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly bool Equals([NotNullWhen(true)] object? obj) + => this.AsROImpl().Equals(obj); /// - public bool Equals(ColorMatrix other) => - this.M11 == other.M11 - && this.M12 == other.M12 - && this.M13 == other.M13 - && this.M14 == other.M14 - && this.M21 == other.M21 - && this.M22 == other.M22 - && this.M23 == other.M23 - && this.M24 == other.M24 - && this.M31 == other.M31 - && this.M32 == other.M32 - && this.M33 == other.M33 - && this.M34 == other.M34 - && this.M41 == other.M41 - && this.M42 == other.M42 - && this.M43 == other.M43 - && this.M44 == other.M44 - && this.M51 == other.M51 - && this.M52 == other.M52 - && this.M53 == other.M53 - && this.M54 == other.M54; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(ColorMatrix other) + => this.AsROImpl().Equals(in other.AsImpl()); /// public override int GetHashCode() - { - HashCode hash = default; - hash.Add(this.M11); - hash.Add(this.M12); - hash.Add(this.M13); - hash.Add(this.M14); - hash.Add(this.M21); - hash.Add(this.M22); - hash.Add(this.M23); - hash.Add(this.M24); - hash.Add(this.M31); - hash.Add(this.M32); - hash.Add(this.M33); - hash.Add(this.M34); - hash.Add(this.M41); - hash.Add(this.M42); - hash.Add(this.M43); - hash.Add(this.M44); - hash.Add(this.M51); - hash.Add(this.M52); - hash.Add(this.M53); - hash.Add(this.M54); - return hash.ToHashCode(); - } + => this.AsROImpl().GetHashCode(); /// public override string ToString() { CultureInfo ci = CultureInfo.CurrentCulture; - return string.Format(ci, "{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}} {{M51:{16} M52:{17} M53:{18} M54:{19}}} }}", - this.M11.ToString(ci), this.M12.ToString(ci), this.M13.ToString(ci), this.M14.ToString(ci), - this.M21.ToString(ci), this.M22.ToString(ci), this.M23.ToString(ci), this.M24.ToString(ci), - this.M31.ToString(ci), this.M32.ToString(ci), this.M33.ToString(ci), this.M34.ToString(ci), - this.M41.ToString(ci), this.M42.ToString(ci), this.M43.ToString(ci), this.M44.ToString(ci), - this.M51.ToString(ci), this.M52.ToString(ci), this.M53.ToString(ci), this.M54.ToString(ci)); + return string.Format( + ci, + "{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}} {{M51:{16} M52:{17} M53:{18} M54:{19}}} }}", + this.M11.ToString(ci), this.M12.ToString(ci), this.M13.ToString(ci), this.M14.ToString(ci), + this.M21.ToString(ci), this.M22.ToString(ci), this.M23.ToString(ci), this.M24.ToString(ci), + this.M31.ToString(ci), this.M32.ToString(ci), this.M33.ToString(ci), this.M34.ToString(ci), + this.M41.ToString(ci), this.M42.ToString(ci), this.M43.ToString(ci), this.M44.ToString(ci), + this.M51.ToString(ci), this.M52.ToString(ci), this.M53.ToString(ci), this.M54.ToString(ci)); } } diff --git a/src/ImageSharp/Primitives/Rectangle.cs b/src/ImageSharp/Primitives/Rectangle.cs index baffbc7f49..e2ae5071ef 100644 --- a/src/ImageSharp/Primitives/Rectangle.cs +++ b/src/ImageSharp/Primitives/Rectangle.cs @@ -195,7 +195,7 @@ public struct Rectangle : IEquatable /// The rectangle. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point Center(Rectangle rectangle) => new(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2)); + public static Point Center(Rectangle rectangle) => new(rectangle.Left + (rectangle.Width >> 1), rectangle.Top + (rectangle.Height >> 1)); // >> 1 is bit-hack for / 2 /// /// Creates a rectangle that represents the intersection between and diff --git a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs index 575ce293ca..f830ddfd09 100644 --- a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; @@ -157,9 +156,9 @@ public static partial class ProcessingExtensions Guard.NotNull(operation, nameof(operation)); source.EnsureNotDisposed(); - var visitor = new ProcessingVisitor(configuration, operation, false); + ProcessingVisitor visitor = new(configuration, operation, false); source.AcceptVisitor(visitor); - return visitor.ResultImage; + return visitor.GetResultImage(); } /// @@ -275,6 +274,8 @@ public static partial class ProcessingExtensions private readonly bool mutate; + private Image? resultImage; + public ProcessingVisitor(Configuration configuration, Action operation, bool mutate) { this.configuration = configuration; @@ -282,7 +283,7 @@ public static partial class ProcessingExtensions this.mutate = mutate; } - public Image ResultImage { get; private set; } + public Image GetResultImage() => this.resultImage!; public void Visit(Image image) where TPixel : unmanaged, IPixel @@ -291,7 +292,7 @@ public static partial class ProcessingExtensions this.configuration.ImageOperationsProvider.CreateImageProcessingContext(this.configuration, image, this.mutate); this.operation(operationsRunner); - this.ResultImage = operationsRunner.GetResultImage(); + this.resultImage = operationsRunner.GetResultImage(); } } } diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs index b710243a56..1c76ea6a45 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs @@ -132,7 +132,7 @@ internal class BinaryThresholdProcessor : ImageProcessor case BinaryThresholdMode.MaxChroma: { - float threshold = this.threshold / 2F; + float threshold = this.threshold * 0.5F; // /2 for (int x = 0; x < rowSpan.Length; x++) { float chroma = GetMaxChroma(span[x]); @@ -149,9 +149,10 @@ internal class BinaryThresholdProcessor : ImageProcessor private static float GetSaturation(Rgb24 rgb) { // Slimmed down RGB => HSL formula. See HslAndRgbConverter. - float r = rgb.R / 255F; - float g = rgb.G / 255F; - float b = rgb.B / 255F; + const float inv255 = 1 / 255F; + float r = rgb.R * inv255; + float g = rgb.G * inv255; + float b = rgb.B * inv255; float max = MathF.Max(r, MathF.Max(g, b)); float min = MathF.Min(r, MathF.Min(g, b)); @@ -162,7 +163,7 @@ internal class BinaryThresholdProcessor : ImageProcessor return 0F; } - float l = (max + min) / 2F; + float l = (max + min) * 0.5F; // /2 if (l <= .5F) { diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 0ceb5bc130..bc023ec450 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -128,19 +128,19 @@ public sealed class BokehBlurProcessor : IImageProcessor int boundsWidth = this.bounds.Width; int kernelSize = this.kernel.Length; - ref int sampleRowBase = ref Unsafe.Add(ref MemoryMarshal.GetReference(this.map.GetRowOffsetSpan()), (y - this.bounds.Y) * kernelSize); + ref int sampleRowBase = ref Unsafe.Add(ref MemoryMarshal.GetReference(this.map.GetRowOffsetSpan()), (uint)((y - this.bounds.Y) * kernelSize)); // The target buffer is zeroed initially and then it accumulates the results // of each partial convolution, so we don't have to clear it here as well ref Vector4 targetBase = ref this.targetValues.GetElementUnsafe(boundsX, y); - ref Complex64 kernelStart = ref this.kernel[0]; - ref Complex64 kernelEnd = ref Unsafe.Add(ref kernelStart, kernelSize); + ref Complex64 kernelStart = ref MemoryMarshal.GetArrayDataReference(this.kernel); + ref Complex64 kernelEnd = ref Unsafe.Add(ref kernelStart, (uint)kernelSize); while (Unsafe.IsAddressLessThan(ref kernelStart, ref kernelEnd)) { // Get the precalculated source sample row for this kernel row and copy to our buffer ref ComplexVector4 sourceBase = ref this.sourceValues.GetElementUnsafe(0, sampleRowBase); - ref ComplexVector4 sourceEnd = ref Unsafe.Add(ref sourceBase, boundsWidth); + ref ComplexVector4 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)boundsWidth); ref Vector4 targetStart = ref targetBase; Complex64 factor = kernelStart; diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 55c03abe6a..e4b0a60ab0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -157,8 +157,8 @@ internal class BokehBlurProcessor : ImageProcessor for (int i = 0; i < this.kernels.Length; i++) { // Compute the resulting complex buffer for the current component - Complex64[] kernel = Unsafe.Add(ref baseRef, i); - Vector4 parameters = Unsafe.Add(ref paramsRef, i); + Complex64[] kernel = Unsafe.Add(ref baseRef, (uint)i); + Vector4 parameters = Unsafe.Add(ref paramsRef, (uint)i); // Horizontal convolution var horizontalOperation = new FirstPassConvolutionRowOperation( @@ -243,9 +243,9 @@ internal class BokehBlurProcessor : ImageProcessor ref Vector4 sourceBase = ref MemoryMarshal.GetReference(span); ref ComplexVector4 targetStart = ref MemoryMarshal.GetReference(targetBuffer); - ref ComplexVector4 targetEnd = ref Unsafe.Add(ref targetStart, span.Length); - ref Complex64 kernelBase = ref this.kernel[0]; - ref Complex64 kernelEnd = ref Unsafe.Add(ref kernelBase, kernelSize); + ref ComplexVector4 targetEnd = ref Unsafe.Add(ref targetStart, (uint)span.Length); + ref Complex64 kernelBase = ref MemoryMarshal.GetArrayDataReference(this.kernel); + ref Complex64 kernelEnd = ref Unsafe.Add(ref kernelBase, (uint)kernelSize); ref int sampleColumnBase = ref MemoryMarshal.GetReference(this.map.GetColumnOffsetSpan()); while (Unsafe.IsAddressLessThan(ref targetStart, ref targetEnd)) @@ -255,7 +255,7 @@ internal class BokehBlurProcessor : ImageProcessor while (Unsafe.IsAddressLessThan(ref kernelStart, ref kernelEnd)) { - Vector4 sample = Unsafe.Add(ref sourceBase, sampleColumnStart - boundsX); + Vector4 sample = Unsafe.Add(ref sourceBase, (uint)(sampleColumnStart - boundsX)); targetStart.Sum(kernelStart * sample); @@ -265,7 +265,7 @@ internal class BokehBlurProcessor : ImageProcessor // Shift the base column sampling reference by one row at the end of each outer // iteration so that the inner tight loop indexing can skip the multiplication - sampleColumnBase = ref Unsafe.Add(ref sampleColumnBase, kernelSize); + sampleColumnBase = ref Unsafe.Add(ref sampleColumnBase, (uint)kernelSize); targetStart = ref Unsafe.Add(ref targetStart, 1); } } @@ -309,7 +309,7 @@ internal class BokehBlurProcessor : ImageProcessor for (int x = 0; x < this.bounds.Width; x++) { - ref Vector4 v = ref Unsafe.Add(ref baseRef, x); + ref Vector4 v = ref Unsafe.Add(ref baseRef, (uint)x); v.X = MathF.Pow(v.X, this.gamma); v.Y = MathF.Pow(v.Y, this.gamma); v.Z = MathF.Pow(v.Z, this.gamma); @@ -399,7 +399,7 @@ internal class BokehBlurProcessor : ImageProcessor for (int x = 0; x < this.bounds.Width; x++) { - ref Vector4 v = ref Unsafe.Add(ref sourceRef, x); + ref Vector4 v = ref Unsafe.Add(ref sourceRef, (uint)x); Vector4 clamp = Numerics.Clamp(v, low, high); v.X = MathF.Pow(clamp.X, this.inverseGamma); v.Y = MathF.Pow(clamp.Y, this.inverseGamma); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs index e5963bd390..2a4a1abf02 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DRowOperation{TPixel}.cs @@ -76,7 +76,7 @@ internal readonly struct Convolution2DRowOperation : IRowOperation targetXBuffer = span.Slice(boundsWidth * 2, boundsWidth); var state = new Convolution2DState(in this.kernelMatrixY, in this.kernelMatrixX, this.map); - ref int sampleRowBase = ref state.GetSampleRow(y - this.bounds.Y); + ref int sampleRowBase = ref state.GetSampleRow((uint)(y - this.bounds.Y)); // Clear the target buffers for each row run. targetYBuffer.Clear(); @@ -87,7 +87,7 @@ internal readonly struct Convolution2DRowOperation : IRowOperation sourceRow; - for (int kY = 0; kY < kernelY.Rows; kY++) + for (uint kY = 0; kY < kernelY.Rows; kY++) { // Get the precalculated source sample row for this kernel row and copy to our buffer. int sampleY = Unsafe.Add(ref sampleRowBase, kY); @@ -96,16 +96,16 @@ internal readonly struct Convolution2DRowOperation : IRowOperation : IRowOperation.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); - for (int x = 0; x < sourceRow.Length; x++) + for (nuint x = 0; x < (uint)sourceRow.Length; x++) { ref Vector4 target = ref Unsafe.Add(ref targetBaseY, x); Vector4 vectorY = target; @@ -142,7 +142,7 @@ internal readonly struct Convolution2DRowOperation : IRowOperation targetXBuffer = span.Slice(boundsWidth * 2, boundsWidth); var state = new Convolution2DState(in this.kernelMatrixY, in this.kernelMatrixX, this.map); - ref int sampleRowBase = ref state.GetSampleRow(y - this.bounds.Y); + ref int sampleRowBase = ref state.GetSampleRow((uint)(y - this.bounds.Y)); // Clear the target buffers for each row run. targetYBuffer.Clear(); @@ -152,7 +152,7 @@ internal readonly struct Convolution2DRowOperation : IRowOperation : IRowOperation : IRowOperation rowOffsetMap; private readonly Span columnOffsetMap; - private readonly int kernelHeight; - private readonly int kernelWidth; + private readonly uint kernelHeight; + private readonly uint kernelWidth; public Convolution2DState( in DenseMatrix kernelY, @@ -24,8 +24,8 @@ internal readonly ref struct Convolution2DState // We check the kernels are the same size upstream. this.KernelY = new ReadOnlyKernel(kernelY); this.KernelX = new ReadOnlyKernel(kernelX); - this.kernelHeight = kernelY.Rows; - this.kernelWidth = kernelY.Columns; + this.kernelHeight = (uint)kernelY.Rows; + this.kernelWidth = (uint)kernelY.Columns; this.rowOffsetMap = map.GetRowOffsetSpan(); this.columnOffsetMap = map.GetColumnOffsetSpan(); } @@ -43,10 +43,10 @@ internal readonly ref struct Convolution2DState } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly ref int GetSampleRow(int row) + public readonly ref int GetSampleRow(uint row) => ref Unsafe.Add(ref MemoryMarshal.GetReference(this.rowOffsetMap), row * this.kernelHeight); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly ref int GetSampleColumn(int column) + public readonly ref int GetSampleColumn(uint column) => ref Unsafe.Add(ref MemoryMarshal.GetReference(this.columnOffsetMap), column * this.kernelWidth); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs index 094a96f787..cc6e1e5fb2 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs @@ -178,9 +178,9 @@ internal class Convolution2PassProcessor : ImageProcessor ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); ref Vector4 targetStart = ref MemoryMarshal.GetReference(targetBuffer); - ref Vector4 targetEnd = ref Unsafe.Add(ref targetStart, sourceBuffer.Length); - ref float kernelBase = ref this.kernel[0]; - ref float kernelEnd = ref Unsafe.Add(ref kernelBase, kernelSize); + ref Vector4 targetEnd = ref Unsafe.Add(ref targetStart, (uint)sourceBuffer.Length); + ref float kernelBase = ref MemoryMarshal.GetArrayDataReference(this.kernel); + ref float kernelEnd = ref Unsafe.Add(ref kernelBase, (uint)kernelSize); ref int sampleColumnBase = ref MemoryMarshal.GetReference(this.map.GetColumnOffsetSpan()); while (Unsafe.IsAddressLessThan(ref targetStart, ref targetEnd)) @@ -190,7 +190,7 @@ internal class Convolution2PassProcessor : ImageProcessor while (Unsafe.IsAddressLessThan(ref kernelStart, ref kernelEnd)) { - Vector4 sample = Unsafe.Add(ref sourceBase, sampleColumnStart - boundsX); + Vector4 sample = Unsafe.Add(ref sourceBase, (uint)(sampleColumnStart - boundsX)); targetStart += kernelStart * sample; @@ -199,7 +199,7 @@ internal class Convolution2PassProcessor : ImageProcessor } targetStart = ref Unsafe.Add(ref targetStart, 1); - sampleColumnBase = ref Unsafe.Add(ref sampleColumnBase, kernelSize); + sampleColumnBase = ref Unsafe.Add(ref sampleColumnBase, (uint)kernelSize); } // Now we need to copy the original alpha values from the source row. @@ -242,9 +242,9 @@ internal class Convolution2PassProcessor : ImageProcessor ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); ref Vector4 targetStart = ref MemoryMarshal.GetReference(targetBuffer); - ref Vector4 targetEnd = ref Unsafe.Add(ref targetStart, sourceBuffer.Length); - ref float kernelBase = ref this.kernel[0]; - ref float kernelEnd = ref Unsafe.Add(ref kernelBase, kernelSize); + ref Vector4 targetEnd = ref Unsafe.Add(ref targetStart, (uint)sourceBuffer.Length); + ref float kernelBase = ref MemoryMarshal.GetArrayDataReference(this.kernel); + ref float kernelEnd = ref Unsafe.Add(ref kernelBase, (uint)kernelSize); ref int sampleColumnBase = ref MemoryMarshal.GetReference(this.map.GetColumnOffsetSpan()); while (Unsafe.IsAddressLessThan(ref targetStart, ref targetEnd)) @@ -254,7 +254,7 @@ internal class Convolution2PassProcessor : ImageProcessor while (Unsafe.IsAddressLessThan(ref kernelStart, ref kernelEnd)) { - Vector4 sample = Unsafe.Add(ref sourceBase, sampleColumnStart - boundsX); + Vector4 sample = Unsafe.Add(ref sourceBase, (uint)(sampleColumnStart - boundsX)); targetStart += kernelStart * sample; @@ -263,7 +263,7 @@ internal class Convolution2PassProcessor : ImageProcessor } targetStart = ref Unsafe.Add(ref targetStart, 1); - sampleColumnBase = ref Unsafe.Add(ref sampleColumnBase, kernelSize); + sampleColumnBase = ref Unsafe.Add(ref sampleColumnBase, (uint)kernelSize); } Numerics.UnPremultiply(targetBuffer); @@ -335,14 +335,14 @@ internal class Convolution2PassProcessor : ImageProcessor Span sourceBuffer = span[..this.bounds.Width]; Span targetBuffer = span[this.bounds.Width..]; - ref int sampleRowBase = ref Unsafe.Add(ref MemoryMarshal.GetReference(this.map.GetRowOffsetSpan()), (y - this.bounds.Y) * kernelSize); + ref int sampleRowBase = ref Unsafe.Add(ref MemoryMarshal.GetReference(this.map.GetRowOffsetSpan()), (uint)((y - this.bounds.Y) * kernelSize)); // Clear the target buffer for each row run. targetBuffer.Clear(); ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer); - ref float kernelStart = ref this.kernel[0]; - ref float kernelEnd = ref Unsafe.Add(ref kernelStart, kernelSize); + ref float kernelStart = ref MemoryMarshal.GetArrayDataReference(this.kernel); + ref float kernelEnd = ref Unsafe.Add(ref kernelStart, (uint)kernelSize); Span sourceRow; while (Unsafe.IsAddressLessThan(ref kernelStart, ref kernelEnd)) @@ -353,7 +353,7 @@ internal class Convolution2PassProcessor : ImageProcessor PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); - ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceBase, sourceBuffer.Length); + ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)sourceBuffer.Length); ref Vector4 targetStart = ref targetBase; float factor = kernelStart; @@ -374,7 +374,7 @@ internal class Convolution2PassProcessor : ImageProcessor PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); { ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); - ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceBase, sourceBuffer.Length); + ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)sourceBuffer.Length); while (Unsafe.IsAddressLessThan(ref sourceBase, ref sourceEnd)) { @@ -400,14 +400,14 @@ internal class Convolution2PassProcessor : ImageProcessor Span sourceBuffer = span[..this.bounds.Width]; Span targetBuffer = span[this.bounds.Width..]; - ref int sampleRowBase = ref Unsafe.Add(ref MemoryMarshal.GetReference(this.map.GetRowOffsetSpan()), (y - this.bounds.Y) * kernelSize); + ref int sampleRowBase = ref Unsafe.Add(ref MemoryMarshal.GetReference(this.map.GetRowOffsetSpan()), (uint)((y - this.bounds.Y) * kernelSize)); // Clear the target buffer for each row run. targetBuffer.Clear(); ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer); - ref float kernelStart = ref this.kernel[0]; - ref float kernelEnd = ref Unsafe.Add(ref kernelStart, kernelSize); + ref float kernelStart = ref MemoryMarshal.GetArrayDataReference(this.kernel); + ref float kernelEnd = ref Unsafe.Add(ref kernelStart, (uint)kernelSize); Span sourceRow; while (Unsafe.IsAddressLessThan(ref kernelStart, ref kernelEnd)) @@ -420,7 +420,7 @@ internal class Convolution2PassProcessor : ImageProcessor Numerics.Premultiply(sourceBuffer); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); - ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceBase, sourceBuffer.Length); + ref Vector4 sourceEnd = ref Unsafe.Add(ref sourceBase, (uint)sourceBuffer.Length); ref Vector4 targetStart = ref targetBase; float factor = kernelStart; diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs index 54dad64a6b..d059ebe030 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -123,7 +123,7 @@ internal class ConvolutionProcessor : ImageProcessor var state = new ConvolutionState(in this.kernel, this.map); int row = y - this.bounds.Y; - ref int sampleRowBase = ref state.GetSampleRow(row); + ref int sampleRowBase = ref state.GetSampleRow((uint)row); if (this.preserveAlpha) { @@ -132,7 +132,7 @@ internal class ConvolutionProcessor : ImageProcessor ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer); Span sourceRow; - for (int kY = 0; kY < state.Kernel.Rows; kY++) + for (uint kY = 0; kY < state.Kernel.Rows; kY++) { // Get the precalculated source sample row for this kernel row and copy to our buffer. int offsetY = Unsafe.Add(ref sampleRowBase, kY); @@ -141,15 +141,15 @@ internal class ConvolutionProcessor : ImageProcessor ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); - for (int x = 0; x < sourceBuffer.Length; x++) + for (uint x = 0; x < (uint)sourceBuffer.Length; x++) { ref int sampleColumnBase = ref state.GetSampleColumn(x); ref Vector4 target = ref Unsafe.Add(ref targetBase, x); - for (int kX = 0; kX < state.Kernel.Columns; kX++) + for (uint kX = 0; kX < state.Kernel.Columns; kX++) { int offsetX = Unsafe.Add(ref sampleColumnBase, kX) - boundsX; - Vector4 sample = Unsafe.Add(ref sourceBase, offsetX); + Vector4 sample = Unsafe.Add(ref sourceBase, (uint)offsetX); target += state.Kernel[kY, kX] * sample; } } @@ -159,7 +159,7 @@ internal class ConvolutionProcessor : ImageProcessor sourceRow = this.sourcePixels.DangerousGetRowSpan(y).Slice(boundsX, boundsWidth); PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceBuffer); - for (int x = 0; x < sourceRow.Length; x++) + for (nuint x = 0; x < (uint)sourceRow.Length; x++) { ref Vector4 target = ref Unsafe.Add(ref targetBase, x); target.W = Unsafe.Add(ref MemoryMarshal.GetReference(sourceBuffer), x).W; @@ -171,7 +171,7 @@ internal class ConvolutionProcessor : ImageProcessor targetBuffer.Clear(); ref Vector4 targetBase = ref MemoryMarshal.GetReference(targetBuffer); - for (int kY = 0; kY < state.Kernel.Rows; kY++) + for (uint kY = 0; kY < state.Kernel.Rows; kY++) { // Get the precalculated source sample row for this kernel row and copy to our buffer. int offsetY = Unsafe.Add(ref sampleRowBase, kY); @@ -181,15 +181,15 @@ internal class ConvolutionProcessor : ImageProcessor Numerics.Premultiply(sourceBuffer); ref Vector4 sourceBase = ref MemoryMarshal.GetReference(sourceBuffer); - for (int x = 0; x < sourceBuffer.Length; x++) + for (uint x = 0; x < (uint)sourceBuffer.Length; x++) { ref int sampleColumnBase = ref state.GetSampleColumn(x); ref Vector4 target = ref Unsafe.Add(ref targetBase, x); - for (int kX = 0; kX < state.Kernel.Columns; kX++) + for (uint kX = 0; kX < state.Kernel.Columns; kX++) { int offsetX = Unsafe.Add(ref sampleColumnBase, kX) - boundsX; - Vector4 sample = Unsafe.Add(ref sourceBase, offsetX); + Vector4 sample = Unsafe.Add(ref sourceBase, (uint)offsetX); target += state.Kernel[kY, kX] * sample; } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionState.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionState.cs index 3d0e551b68..6663c45021 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionState.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionState.cs @@ -13,16 +13,16 @@ internal readonly ref struct ConvolutionState { private readonly Span rowOffsetMap; private readonly Span columnOffsetMap; - private readonly int kernelHeight; - private readonly int kernelWidth; + private readonly uint kernelHeight; + private readonly uint kernelWidth; public ConvolutionState( in DenseMatrix kernel, KernelSamplingMap map) { this.Kernel = new ReadOnlyKernel(kernel); - this.kernelHeight = kernel.Rows; - this.kernelWidth = kernel.Columns; + this.kernelHeight = (uint)kernel.Rows; + this.kernelWidth = (uint)kernel.Columns; this.rowOffsetMap = map.GetRowOffsetSpan(); this.columnOffsetMap = map.GetColumnOffsetSpan(); } @@ -34,10 +34,10 @@ internal readonly ref struct ConvolutionState } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly ref int GetSampleRow(int row) + public readonly ref int GetSampleRow(uint row) => ref Unsafe.Add(ref MemoryMarshal.GetReference(this.rowOffsetMap), row * this.kernelHeight); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly ref int GetSampleColumn(int column) + public readonly ref int GetSampleColumn(uint column) => ref Unsafe.Add(ref MemoryMarshal.GetReference(this.columnOffsetMap), column * this.kernelWidth); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index 75e8e98e91..cbf893915c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -98,8 +98,8 @@ internal class EdgeDetectorCompassProcessor : ImageProcessor { private readonly Buffer2D targetPixels; private readonly Buffer2D passPixels; - private readonly int minX; - private readonly int maxX; + private readonly uint minX; + private readonly uint maxX; [MethodImpl(InliningOptions.ShortMethod)] public RowOperation( @@ -109,8 +109,8 @@ internal class EdgeDetectorCompassProcessor : ImageProcessor { this.targetPixels = targetPixels; this.passPixels = passPixels; - this.minX = bounds.X; - this.maxX = bounds.Right; + this.minX = (uint)bounds.X; + this.maxX = (uint)bounds.Right; } /// @@ -120,7 +120,7 @@ internal class EdgeDetectorCompassProcessor : ImageProcessor ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(this.passPixels.DangerousGetRowSpan(y)); ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(this.targetPixels.DangerousGetRowSpan(y)); - for (int x = this.minX; x < this.maxX; x++) + for (nuint x = this.minX; x < this.maxX; x++) { // Grab the max components of the two pixels ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, x); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernel.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernel.cs index 35aa933cf7..b225b55e5f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernel.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernel.cs @@ -49,7 +49,7 @@ internal readonly ref struct Kernel { this.CheckCoordinates(row, column); ref T vBase = ref MemoryMarshal.GetReference(this.values); - return Unsafe.Add(ref vBase, (row * this.Columns) + column); + return Unsafe.Add(ref vBase, (uint)((row * this.Columns) + column)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -57,7 +57,7 @@ internal readonly ref struct Kernel { this.CheckCoordinates(row, column); ref T vBase = ref MemoryMarshal.GetReference(this.values); - Unsafe.Add(ref vBase, (row * this.Columns) + column) = value; + Unsafe.Add(ref vBase, (uint)((row * this.Columns) + column)) = value; } } @@ -66,7 +66,7 @@ internal readonly ref struct Kernel { this.CheckIndex(index); ref T vBase = ref MemoryMarshal.GetReference(this.values); - Unsafe.Add(ref vBase, index) = value; + Unsafe.Add(ref vBase, (uint)index) = value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs b/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs index 8128d01196..7653aeaa96 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs @@ -92,7 +92,7 @@ internal sealed class KernelSamplingMap : IDisposable int chunkBase = chunk * kernelSize; for (int i = 0; i < kernelSize; i++) { - Unsafe.Add(ref spanBase, chunkBase + i) = chunk + i + min - radius; + Unsafe.Add(ref spanBase, (uint)(chunkBase + i)) = chunk + i + min - radius; } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/MedianConvolutionState.cs b/src/ImageSharp/Processing/Processors/Convolution/MedianConvolutionState.cs index d557896e3c..137334c29e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/MedianConvolutionState.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/MedianConvolutionState.cs @@ -36,9 +36,9 @@ internal readonly ref struct MedianConvolutionState [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly ref int GetSampleRow(int row) - => ref Unsafe.Add(ref MemoryMarshal.GetReference(this.rowOffsetMap), row * this.kernelHeight); + => ref Unsafe.Add(ref MemoryMarshal.GetReference(this.rowOffsetMap), (uint)(row * this.kernelHeight)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly ref int GetSampleColumn(int column) - => ref Unsafe.Add(ref MemoryMarshal.GetReference(this.columnOffsetMap), column * this.kernelWidth); + => ref Unsafe.Add(ref MemoryMarshal.GetReference(this.columnOffsetMap), (uint)(column * this.kernelWidth)); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/MedianRowOperation{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/MedianRowOperation{TPixel}.cs index dbb62f0c58..ca2cabab55 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/MedianRowOperation{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/MedianRowOperation{TPixel}.cs @@ -75,7 +75,7 @@ internal readonly struct MedianRowOperation : IRowOperation // First convert the required source rows to Vector4. for (int i = 0; i < this.kernelSize; i++) { - int currentYIndex = Unsafe.Add(ref sampleRowBase, i); + int currentYIndex = Unsafe.Add(ref sampleRowBase, (uint)i); Span sourceRow = this.sourcePixels.DangerousGetRowSpan(currentYIndex).Slice(boundsX, boundsWidth); Span sourceVectorRow = sourceVectorBuffer.Slice(i * boundsWidth, boundsWidth); PixelOperations.Instance.ToVector4(this.configuration, sourceRow, sourceVectorRow); @@ -87,15 +87,15 @@ internal readonly struct MedianRowOperation : IRowOperation { int index = 0; ref int sampleColumnBase = ref state.GetSampleColumn(x); - ref Vector4 target = ref Unsafe.Add(ref targetBase, x); + ref Vector4 target = ref Unsafe.Add(ref targetBase, (uint)x); for (int kY = 0; kY < state.Kernel.Rows; kY++) { Span sourceRow = sourceVectorBuffer[(kY * boundsWidth)..]; ref Vector4 sourceRowBase = ref MemoryMarshal.GetReference(sourceRow); for (int kX = 0; kX < state.Kernel.Columns; kX++) { - int currentXIndex = Unsafe.Add(ref sampleColumnBase, kX) - boundsX; - Vector4 pixel = Unsafe.Add(ref sourceRowBase, currentXIndex); + int currentXIndex = Unsafe.Add(ref sampleColumnBase, (uint)kX) - boundsX; + Vector4 pixel = Unsafe.Add(ref sourceRowBase, (uint)currentXIndex); state.Kernel.SetValue(index, pixel); index++; } @@ -111,15 +111,15 @@ internal readonly struct MedianRowOperation : IRowOperation { int index = 0; ref int sampleColumnBase = ref state.GetSampleColumn(x); - ref Vector4 target = ref Unsafe.Add(ref targetBase, x); + ref Vector4 target = ref Unsafe.Add(ref targetBase, (uint)x); for (int kY = 0; kY < state.Kernel.Rows; kY++) { Span sourceRow = sourceVectorBuffer[(kY * boundsWidth)..]; ref Vector4 sourceRowBase = ref MemoryMarshal.GetReference(sourceRow); for (int kX = 0; kX < state.Kernel.Columns; kX++) { - int currentXIndex = Unsafe.Add(ref sampleColumnBase, kX) - boundsX; - Vector4 pixel = Unsafe.Add(ref sourceRowBase, currentXIndex); + int currentXIndex = Unsafe.Add(ref sampleColumnBase, (uint)kX) - boundsX; + Vector4 pixel = Unsafe.Add(ref sourceRowBase, (uint)currentXIndex); state.Kernel.SetValue(index, pixel); index++; } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs index 08dee86a4e..a680393c8c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelDataProvider.cs @@ -134,7 +134,7 @@ internal static class BokehBlurKernelDataProvider ref Vector4 baseRef = ref MemoryMarshal.GetReference(kernelParameters.AsSpan()); for (int i = 0; i < kernelParameters.Length; i++) { - ref Vector4 paramsRef = ref Unsafe.Add(ref baseRef, i); + ref Vector4 paramsRef = ref Unsafe.Add(ref baseRef, (uint)i); kernels[i] = CreateComplex1DKernel(radius, kernelSize, kernelsScale, paramsRef.X, paramsRef.Y); } @@ -167,7 +167,7 @@ internal static class BokehBlurKernelDataProvider value *= value; // Fill in the complex kernel values - Unsafe.Add(ref baseRef, i) = new Complex64( + Unsafe.Add(ref baseRef, (uint)i) = new Complex64( MathF.Exp(-a * value) * MathF.Cos(b * value), MathF.Exp(-a * value) * MathF.Sin(b * value)); } @@ -190,17 +190,17 @@ internal static class BokehBlurKernelDataProvider for (int i = 0; i < kernelParameters.Length; i++) { - ref Complex64[] kernelRef = ref Unsafe.Add(ref baseKernelsRef, i); + ref Complex64[] kernelRef = ref Unsafe.Add(ref baseKernelsRef, (uint)i); int length = kernelRef.Length; - ref Complex64 valueRef = ref kernelRef[0]; - ref Vector4 paramsRef = ref Unsafe.Add(ref baseParamsRef, i); + ref Complex64 valueRef = ref MemoryMarshal.GetArrayDataReference(kernelRef); + ref Vector4 paramsRef = ref Unsafe.Add(ref baseParamsRef, (uint)i); for (int j = 0; j < length; j++) { for (int k = 0; k < length; k++) { - ref Complex64 jRef = ref Unsafe.Add(ref valueRef, j); - ref Complex64 kRef = ref Unsafe.Add(ref valueRef, k); + ref Complex64 jRef = ref Unsafe.Add(ref valueRef, (uint)j); + ref Complex64 kRef = ref Unsafe.Add(ref valueRef, (uint)k); total += (paramsRef.Z * ((jRef.Real * kRef.Real) - (jRef.Imaginary * kRef.Imaginary))) + (paramsRef.W * ((jRef.Real * kRef.Imaginary) + (jRef.Imaginary * kRef.Real))); @@ -212,13 +212,13 @@ internal static class BokehBlurKernelDataProvider float scalar = 1f / MathF.Sqrt(total); for (int i = 0; i < kernelsSpan.Length; i++) { - ref Complex64[] kernelsRef = ref Unsafe.Add(ref baseKernelsRef, i); + ref Complex64[] kernelsRef = ref Unsafe.Add(ref baseKernelsRef, (uint)i); int length = kernelsRef.Length; - ref Complex64 valueRef = ref kernelsRef[0]; + ref Complex64 valueRef = ref MemoryMarshal.GetArrayDataReference(kernelsRef); for (int j = 0; j < length; j++) { - Unsafe.Add(ref valueRef, j) *= scalar; + Unsafe.Add(ref valueRef, (uint)j) *= scalar; } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ReadOnlyKernel.cs b/src/ImageSharp/Processing/Processors/Convolution/ReadOnlyKernel.cs index 641a8ae8b0..2a09589bda 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ReadOnlyKernel.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ReadOnlyKernel.cs @@ -17,24 +17,24 @@ internal readonly ref struct ReadOnlyKernel public ReadOnlyKernel(DenseMatrix matrix) { - this.Columns = matrix.Columns; - this.Rows = matrix.Rows; + this.Columns = (uint)matrix.Columns; + this.Rows = (uint)matrix.Rows; this.values = matrix.Span; } - public int Columns + public uint Columns { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } - public int Rows + public uint Rows { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } - public float this[int row, int column] + public float this[uint row, uint column] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get @@ -46,14 +46,14 @@ internal readonly ref struct ReadOnlyKernel } [Conditional("DEBUG")] - private void CheckCoordinates(int row, int column) + private void CheckCoordinates(uint row, uint column) { - if (row < 0 || row >= this.Rows) + if (row >= this.Rows) { throw new ArgumentOutOfRangeException(nameof(row), row, $"{row} is outwith the matrix bounds."); } - if (column < 0 || column >= this.Columns) + if (column >= this.Columns) { throw new ArgumentOutOfRangeException(nameof(column), column, $"{column} is outwith the matrix bounds."); } diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs index 3a869a0a28..754aac90ea 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs @@ -114,8 +114,8 @@ public readonly partial struct ErrorDither : IDither, IEquatable, I for (int x = bounds.Left; x < bounds.Right; x++) { - TPixel sourcePixel = Unsafe.Add(ref sourceRowRef, x); - Unsafe.Add(ref destinationRowRef, x - offsetX) = quantizer.GetQuantizedColor(sourcePixel, out TPixel transformed); + TPixel sourcePixel = Unsafe.Add(ref sourceRowRef, (uint)x); + Unsafe.Add(ref destinationRowRef, (uint)(x - offsetX)) = quantizer.GetQuantizedColor(sourcePixel, out TPixel transformed); this.Dither(source, bounds, sourcePixel, transformed, x, y, scale); } } @@ -142,7 +142,7 @@ public readonly partial struct ErrorDither : IDither, IEquatable, I ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(sourceBuffer.DangerousGetRowSpan(y)); for (int x = bounds.Left; x < bounds.Right; x++) { - ref TPixel sourcePixel = ref Unsafe.Add(ref sourceRowRef, x); + ref TPixel sourcePixel = ref Unsafe.Add(ref sourceRowRef, (uint)x); TPixel transformed = Unsafe.AsRef(processor).GetPaletteColor(sourcePixel); this.Dither(source, bounds, sourcePixel, transformed, x, y, scale); sourcePixel = transformed; diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 67964f0ebd..6352230de2 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -107,9 +107,9 @@ internal class OilPaintingProcessor : ImageProcessor ref float binsRef = ref bins.GetReference(); ref int intensityBinRef = ref Unsafe.As(ref binsRef); - ref float redBinRef = ref Unsafe.Add(ref binsRef, this.levels); - ref float blueBinRef = ref Unsafe.Add(ref redBinRef, this.levels); - ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, this.levels); + ref float redBinRef = ref Unsafe.Add(ref binsRef, (uint)this.levels); + ref float blueBinRef = ref Unsafe.Add(ref redBinRef, (uint)this.levels); + ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, (uint)this.levels); for (int y = rows.Min; y < rows.Max; y++) { @@ -148,21 +148,21 @@ internal class OilPaintingProcessor : ImageProcessor int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (this.levels - 1)); - Unsafe.Add(ref intensityBinRef, currentIntensity)++; - Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; - Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; - Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; + Unsafe.Add(ref intensityBinRef, (uint)currentIntensity)++; + Unsafe.Add(ref redBinRef, (uint)currentIntensity) += sourceRed; + Unsafe.Add(ref blueBinRef, (uint)currentIntensity) += sourceBlue; + Unsafe.Add(ref greenBinRef, (uint)currentIntensity) += sourceGreen; - if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) + if (Unsafe.Add(ref intensityBinRef, (uint)currentIntensity) > maxIntensity) { - maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); + maxIntensity = Unsafe.Add(ref intensityBinRef, (uint)currentIntensity); maxIndex = currentIntensity; } } - float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); - float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); - float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); + float red = MathF.Abs(Unsafe.Add(ref redBinRef, (uint)maxIndex) / maxIntensity); + float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, (uint)maxIndex) / maxIntensity); + float green = MathF.Abs(Unsafe.Add(ref greenBinRef, (uint)maxIndex) / maxIntensity); float alpha = sourceRowVector4Span[x].W; targetRowVector4Span[x] = new Vector4(red, green, blue, alpha); diff --git a/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs index 4b8fd9056e..93e600106a 100644 --- a/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/OpaqueProcessor{TPixel}.cs @@ -61,7 +61,7 @@ internal sealed class OpaqueProcessor : ImageProcessor for (int x = 0; x < this.bounds.Width; x++) { - ref Vector4 v = ref Unsafe.Add(ref baseRef, x); + ref Vector4 v = ref Unsafe.Add(ref baseRef, (uint)x); v.W = 1F; } diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index e2272db039..0baa87a611 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -59,8 +59,8 @@ internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualiz int tileWidth = (int)MathF.Ceiling(sourceWidth / (float)this.Tiles); int tileHeight = (int)MathF.Ceiling(sourceHeight / (float)this.Tiles); int tileCount = this.Tiles; - int halfTileWidth = tileWidth / 2; - int halfTileHeight = tileHeight / 2; + int halfTileWidth = (int)((uint)tileWidth / 2); + int halfTileHeight = (int)((uint)tileHeight / 2); int luminanceLevels = this.LuminanceLevels; // The image is split up into tiles. For each tile the cumulative distribution function will be calculated. @@ -176,7 +176,7 @@ internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualiz int xEnd, int luminanceLevels) { - int halfTileHeight = tileHeight / 2; + int halfTileHeight = (int)((uint)tileHeight / 2); int cdfY = 0; int y = halfTileHeight; @@ -228,7 +228,7 @@ internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualiz int yEnd, int luminanceLevels) { - int halfTileWidth = tileWidth / 2; + int halfTileWidth = (int)((uint)tileWidth / 2); int cdfX = 0; int x = halfTileWidth; diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs index 3aaf2631ae..d2bec0b49b 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs @@ -59,7 +59,7 @@ internal class AdaptiveHistogramEqualizationSlidingWindowProcessor : His int tileWidth = source.Width / this.Tiles; int tileHeight = tileWidth; int pixelInTile = tileWidth * tileHeight; - int halfTileHeight = tileHeight / 2; + int halfTileHeight = (int)((uint)tileHeight / 2); int halfTileWidth = halfTileHeight; SlidingWindowInfos slidingWindowInfos = new(tileWidth, tileHeight, halfTileWidth, halfTileHeight, pixelInTile); @@ -254,10 +254,10 @@ internal class AdaptiveHistogramEqualizationSlidingWindowProcessor : His [MethodImpl(InliningOptions.ShortMethod)] private static void AddPixelsToHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) { - for (nint idx = 0; idx < length; idx++) + for (nuint idx = 0; idx < (uint)length; idx++) { int luminance = ColorNumerics.GetBT709Luminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); - Unsafe.Add(ref histogramBase, luminance)++; + Unsafe.Add(ref histogramBase, (uint)luminance)++; } } @@ -271,10 +271,10 @@ internal class AdaptiveHistogramEqualizationSlidingWindowProcessor : His [MethodImpl(InliningOptions.ShortMethod)] private static void RemovePixelsFromHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) { - for (int idx = 0; idx < length; idx++) + for (nuint idx = 0; idx < (uint)length; idx++) { int luminance = ColorNumerics.GetBT709Luminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); - Unsafe.Add(ref histogramBase, luminance)--; + Unsafe.Add(ref histogramBase, (uint)luminance)--; } } @@ -382,7 +382,7 @@ internal class AdaptiveHistogramEqualizationSlidingWindowProcessor : His // Map the current pixel to the new equalized value. int luminance = GetLuminance(this.source[x, y], this.processor.LuminanceLevels); - float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; + float luminanceEqualized = Unsafe.Add(ref cdfBase, (uint)luminance) / numberOfPixelsMinusCdfMin; this.targetPixels[x, y].FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, this.source[x, y].ToVector4().W)); // Remove top most row from the histogram, mirroring rows which exceeds the borders. diff --git a/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs index c07ac3aa34..6f4493f951 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs @@ -148,12 +148,12 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow); @@ -209,7 +209,7 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor : HistogramEqualizationProcessor.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow); diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index 7e9e064642..e7433899bf 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -129,10 +129,10 @@ internal class GlobalHistogramEqualizationProcessor : HistogramEqualizat for (int x = 0; x < this.bounds.Width; x++) { - var vector = Unsafe.Add(ref vectorRef, x); + var vector = Unsafe.Add(ref vectorRef, (uint)x); int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels); - float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / noOfPixelsMinusCdfMin; - Unsafe.Add(ref vectorRef, x) = new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, vector.W); + float luminanceEqualized = Unsafe.Add(ref cdfBase, (uint)luminance) / noOfPixelsMinusCdfMin; + Unsafe.Add(ref vectorRef, (uint)x) = new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, vector.W); } PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow); diff --git a/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs index 8895fdc612..0a8690ba70 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs @@ -56,9 +56,9 @@ internal readonly struct GrayscaleLevelsRowOperation : IRowOperation : ImageProcessor< int cdfMin = 0; bool cdfMinFound = false; - for (int i = 0; i <= maxIdx; i++) + for (nuint i = 0; i <= (uint)maxIdx; i++) { histSum += Unsafe.Add(ref histogramBase, i); if (!cdfMinFound && histSum != 0) @@ -101,7 +101,7 @@ internal abstract class HistogramEqualizationProcessor : ImageProcessor< int sumOverClip = 0; ref int histogramBase = ref MemoryMarshal.GetReference(histogram); - for (int i = 0; i < histogram.Length; i++) + for (nuint i = 0; i < (uint)histogram.Length; i++) { ref int histogramLevel = ref Unsafe.Add(ref histogramBase, i); if (histogramLevel > clipLimit) @@ -115,7 +115,7 @@ internal abstract class HistogramEqualizationProcessor : ImageProcessor< int addToEachBin = sumOverClip > 0 ? (int)MathF.Floor(sumOverClip / this.luminanceLevelsFloat) : 0; if (addToEachBin > 0) { - for (int i = 0; i < histogram.Length; i++) + for (nuint i = 0; i < (uint)histogram.Length; i++) { Unsafe.Add(ref histogramBase, i) += addToEachBin; } @@ -124,8 +124,8 @@ internal abstract class HistogramEqualizationProcessor : ImageProcessor< int residual = sumOverClip - (addToEachBin * this.LuminanceLevels); if (residual != 0) { - int residualStep = Math.Max(this.LuminanceLevels / residual, 1); - for (int i = 0; i < this.LuminanceLevels && residual > 0; i += residualStep, residual--) + uint residualStep = (uint)Math.Max(this.LuminanceLevels / residual, 1); + for (nuint i = 0; i < (uint)this.LuminanceLevels && residual > 0; i += residualStep, residual--) { ref int histogramLevel = ref Unsafe.Add(ref histogramBase, i); histogramLevel++; diff --git a/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs index a8cf4d3e91..786248339b 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs @@ -75,7 +75,7 @@ internal sealed class EuclideanPixelMap : IDisposable return this.GetClosestColorSlow(rgba, ref paletteRef, out match); } - match = Unsafe.Add(ref paletteRef, index); + match = Unsafe.Add(ref paletteRef, (ushort)index); return index; } @@ -119,7 +119,7 @@ internal sealed class EuclideanPixelMap : IDisposable // Now I have the index, pop it into the cache for next time this.cache.Add(rgba, (byte)index); - match = Unsafe.Add(ref paletteRef, index); + match = Unsafe.Add(ref paletteRef, (uint)index); return index; } diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs index edf293d39e..a231d6dee7 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs @@ -684,7 +684,7 @@ internal struct WuQuantizer : IQuantizer using IMemoryOwner vvOwner = this.Configuration.MemoryAllocator.Allocate(this.maxColors); Span vv = vvOwner.GetSpan(); - ref Box cube = ref this.colorCube[0]; + ref Box cube = ref MemoryMarshal.GetArrayDataReference(this.colorCube); cube.RMin = cube.GMin = cube.BMin = cube.AMin = 0; cube.RMax = cube.GMax = cube.BMax = IndexCount - 1; cube.AMax = IndexAlphaCount - 1; diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs index abb8beccdf..30c279e593 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs @@ -19,7 +19,7 @@ public class AffineTransformProcessor : CloningImageProcessor public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size targetDimensions) { Guard.NotNull(sampler, nameof(sampler)); - Guard.MustBeValueType(sampler, nameof(sampler)); + Guard.MustBeValueType(sampler); if (TransformUtils.IsDegenerate(matrix)) { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs index 11befd5dac..14da3ac890 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs @@ -54,7 +54,7 @@ internal class FlipProcessor : ImageProcessor using IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(source.Width); Span temp = tempBuffer.Memory.Span; - for (int yTop = 0; yTop < height / 2; yTop++) + for (int yTop = 0; yTop < (int)((uint)height / 2); yTop++) { int yBottom = height - yTop - 1; Span topRow = source.DangerousGetRowSpan(yBottom); diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs index 9b0b979e69..9e9507b737 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs @@ -19,7 +19,7 @@ public sealed class ProjectiveTransformProcessor : CloningImageProcessor public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size targetDimensions) { Guard.NotNull(sampler, nameof(sampler)); - Guard.MustBeValueType(sampler, nameof(sampler)); + Guard.MustBeValueType(sampler); if (TransformUtils.IsDegenerate(matrix)) { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs index d03a694ba5..d90f948b6f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs @@ -105,11 +105,11 @@ internal static class ResizeHelper switch (options.Position) { case AnchorPositionMode.Left: - targetY = (height - sourceHeight) / 2; + targetY = (int)((uint)(height - sourceHeight) / 2); targetX = 0; break; case AnchorPositionMode.Right: - targetY = (height - sourceHeight) / 2; + targetY = (int)((uint)(height - sourceHeight) / 2); targetX = width - sourceWidth; break; case AnchorPositionMode.TopRight: @@ -118,7 +118,7 @@ internal static class ResizeHelper break; case AnchorPositionMode.Top: targetY = 0; - targetX = (width - sourceWidth) / 2; + targetX = (int)((uint)(width - sourceWidth) / 2); break; case AnchorPositionMode.TopLeft: targetY = 0; @@ -130,15 +130,15 @@ internal static class ResizeHelper break; case AnchorPositionMode.Bottom: targetY = height - sourceHeight; - targetX = (width - sourceWidth) / 2; + targetX = (int)((uint)(width - sourceWidth) / 2); break; case AnchorPositionMode.BottomLeft: targetY = height - sourceHeight; targetX = 0; break; default: - targetY = (height - sourceHeight) / 2; - targetX = (width - sourceWidth) / 2; + targetY = (int)((uint)(height - sourceHeight) / 2); + targetX = (int)((uint)(width - sourceWidth) / 2); break; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs index e492011788..c1907bb520 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs @@ -100,7 +100,7 @@ internal partial class ResizeKernelMap : IDisposable /// Returns a for an index value between 0 and DestinationSize - 1. /// [MethodImpl(InliningOptions.ShortMethod)] - internal ref ResizeKernel GetKernel(nint destIdx) => ref this.kernels[destIdx]; + internal ref ResizeKernel GetKernel(nuint destIdx) => ref this.kernels[(int)destIdx]; /// /// Computes the weights to apply at each pixel when resizing. diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index b7651c03f3..719622a28f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -17,7 +17,7 @@ public class ResizeProcessor : CloningImageProcessor { Guard.NotNull(options, nameof(options)); Guard.NotNull(options.Sampler, nameof(options.Sampler)); - Guard.MustBeValueType(options.Sampler, nameof(options.Sampler)); + Guard.MustBeValueType(options.Sampler); (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options); diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 5cd9976a58..cce27a401c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -119,7 +119,7 @@ internal sealed class ResizeWorker : IDisposable for (int y = rowInterval.Min; y < rowInterval.Max; y++) { // Ensure offsets are normalized for cropping and padding. - ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - this.targetOrigin.Y); + ResizeKernel kernel = this.verticalKernelMap.GetKernel((uint)(y - this.targetOrigin.Y)); while (kernel.StartIndex + kernel.Length > this.currentWindow.Max) { @@ -132,9 +132,9 @@ internal sealed class ResizeWorker : IDisposable ref Vector4 fpBase = ref transposedFirstPassBufferSpan[top]; - for (nint x = 0; x < (right - left); x++) + for (nuint x = 0; x < (uint)(right - left); x++) { - ref Vector4 firstPassColumnBase = ref Unsafe.Add(ref fpBase, x * this.workerHeight); + ref Vector4 firstPassColumnBase = ref Unsafe.Add(ref fpBase, x * (uint)this.workerHeight); // Destination color components Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(ref firstPassColumnBase); @@ -169,9 +169,9 @@ internal sealed class ResizeWorker : IDisposable Span tempRowSpan = this.tempRowBuffer.GetSpan(); Span transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.DangerousGetSingleSpan(); - int left = this.targetWorkingRect.Left; - int right = this.targetWorkingRect.Right; - int targetOriginX = this.targetOrigin.X; + nuint left = (uint)this.targetWorkingRect.Left; + nuint right = (uint)this.targetWorkingRect.Right; + nuint targetOriginX = (uint)this.targetOrigin.X; for (int y = calculationInterval.Min; y < calculationInterval.Max; y++) { Span sourceRow = this.source.DangerousGetRowSpan(y); @@ -186,13 +186,13 @@ internal sealed class ResizeWorker : IDisposable // Span firstPassSpan = transposedFirstPassBufferSpan.Slice(y - this.currentWindow.Min); ref Vector4 firstPassBaseRef = ref transposedFirstPassBufferSpan[y - this.currentWindow.Min]; - for (nint x = left, z = 0; x < right; x++, z++) + for (nuint x = left, z = 0; x < right; x++, z++) { ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - targetOriginX); // optimization for: // firstPassSpan[x * this.workerHeight] = kernel.Convolve(tempRowSpan); - Unsafe.Add(ref firstPassBaseRef, z * this.workerHeight) = kernel.Convolve(tempRowSpan); + Unsafe.Add(ref firstPassBaseRef, z * (uint)this.workerHeight) = kernel.Convolve(tempRowSpan); } } } diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs index 44d6d8e720..ad0888ad77 100644 --- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs +++ b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing; /// public class ProjectiveTransformBuilder { - private readonly List> matrixFactories = new List>(); + private readonly List> matrixFactories = new(); /// /// Prepends a matrix that performs a tapering projective transform. @@ -313,7 +313,7 @@ public class ProjectiveTransformBuilder Guard.MustBeGreaterThan(sourceRectangle.Height, 0, nameof(sourceRectangle)); // Translate the origin matrix to cater for source rectangle offsets. - var matrix = Matrix4x4.CreateTranslation(new Vector3(-sourceRectangle.Location, 0)); + Matrix4x4 matrix = Matrix4x4.CreateTranslation(new Vector3(-sourceRectangle.Location, 0)); Size size = sourceRectangle.Size; diff --git a/tests/ImageSharp.Benchmarks/Bulk/ColorMatrixTransforms.cs b/tests/ImageSharp.Benchmarks/Bulk/ColorMatrixTransforms.cs new file mode 100644 index 0000000000..9147e36497 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Bulk/ColorMatrixTransforms.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp.Benchmarks.Bulk; + +public class ColorMatrixTransforms +{ + private static readonly Vector4[] Vectors = Vector4Factory.CreateVectors(); + + [Benchmark(Baseline = true)] + public void Transform() + { + ColorMatrix matrix = KnownFilterMatrices.CreateHueFilter(45F); + for (int i = 0; i < Vectors.Length; i++) + { + ref Vector4 input = ref Vectors[i]; + ColorNumerics.Transform(ref input, ref matrix); + } + } + + [Benchmark] + public void Transform_Span() + { + ColorMatrix matrix = KnownFilterMatrices.CreateHueFilter(45F); + ColorNumerics.Transform(Vectors.AsSpan(), ref matrix); + } +} diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/FromRgba32Bytes.cs b/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs similarity index 92% rename from tests/ImageSharp.Benchmarks/Color/Bulk/FromRgba32Bytes.cs rename to tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs index e27bf07e99..bd938c9da9 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/FromRgba32Bytes.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; public abstract class FromRgba32Bytes where TPixel : unmanaged, IPixel @@ -64,7 +64,7 @@ public abstract class FromRgba32Bytes [Benchmark] public void OptimizedBulk() { - PixelOperations.Instance.FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); + PixelOperations.Instance.FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs similarity index 95% rename from tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs rename to tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs index d1cb616780..7e6cec2018 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromVector4.cs @@ -12,7 +12,7 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.ShortCore31))] public abstract class FromVector4 @@ -47,7 +47,7 @@ public abstract class FromVector4 { ref Vector4 s = ref MemoryMarshal.GetReference(this.source.GetSpan()); ref TPixel d = ref MemoryMarshal.GetReference(this.destination.GetSpan()); - for (int i = 0; i < this.Count; i++) + for (nuint i = 0; i < (uint)this.Count; i++) { Unsafe.Add(ref d, i).FromVector4(Unsafe.Add(ref s, i)); } @@ -103,10 +103,9 @@ public class FromVector4Rgba32 : FromVector4 Span src = MemoryMarshal.Cast(this.source.GetSpan()); Span dest = MemoryMarshal.Cast(this.destination.GetSpan()); - int n = dest.Length / Vector.Count; + nuint n = (uint)dest.Length / (uint)Vector.Count; - ref Vector256 sourceBase = - ref Unsafe.As>(ref MemoryMarshal.GetReference(src)); + ref Vector256 sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(src)); ref Vector256 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); ref byte maskBase = ref MemoryMarshal.GetReference(PermuteMaskDeinterleave8x32); @@ -114,7 +113,7 @@ public class FromVector4Rgba32 : FromVector4 var maxBytes = Vector256.Create(255f); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { ref Vector256 s = ref Unsafe.Add(ref sourceBase, i * 4); diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4_Rgb24.cs b/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs similarity index 98% rename from tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4_Rgb24.cs rename to tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs index 2effbd1d59..fe0d2a10a7 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4_Rgb24.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromVector4_Rgb24.cs @@ -4,7 +4,7 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.ShortMultiFramework))] public class FromVector4_Rgb24 : FromVector4 diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/Pad3Shuffle4Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/Pad3Shuffle4Channel.cs similarity index 54% rename from tests/ImageSharp.Benchmarks/Color/Bulk/Pad3Shuffle4Channel.cs rename to tests/ImageSharp.Benchmarks/Bulk/Pad3Shuffle4Channel.cs index df308cdd4b..1b6663e70e 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/Pad3Shuffle4Channel.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/Pad3Shuffle4Channel.cs @@ -3,13 +3,12 @@ using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.HwIntrinsics_SSE_AVX))] public class Pad3Shuffle4Channel { - private static readonly DefaultPad3Shuffle4 Control = new DefaultPad3Shuffle4(1, 0, 3, 2); - private static readonly XYZWPad3Shuffle4 ControlFast = default; + private static readonly DefaultPad3Shuffle4 Control = new(SimdUtils.Shuffle.MMShuffle1032); private byte[] source; private byte[] destination; @@ -26,15 +25,11 @@ public class Pad3Shuffle4Channel [Benchmark] public void Pad3Shuffle4() - { - SimdUtils.Pad3Shuffle4(this.source, this.destination, Control); - } + => SimdUtils.Pad3Shuffle4(this.source, this.destination, Control); [Benchmark] public void Pad3Shuffle4FastFallback() - { - SimdUtils.Pad3Shuffle4(this.source, this.destination, ControlFast); - } + => SimdUtils.Pad3Shuffle4(this.source, this.destination, default(XYZWPad3Shuffle4)); } // 2020-10-30 @@ -83,3 +78,50 @@ public class Pad3Shuffle4Channel // | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 220.37 ns | 1.601 ns | 1.419 ns | 220.13 ns | 1.00 | 0.00 | - | - | - | - | // | Pad3Shuffle4FastFallback | 2. AVX | Empty | 1536 | 111.54 ns | 2.173 ns | 2.901 ns | 111.27 ns | 0.51 | 0.01 | - | - | - | - | // | Pad3Shuffle4FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 110.23 ns | 0.456 ns | 0.427 ns | 110.25 ns | 0.50 | 0.00 | - | - | - | - | + +// 2023-02-21 +// ########## +// +// BenchmarkDotNet=v0.13.0, OS=Windows 10.0.22621 +// 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores +// .NET SDK= 7.0.103 +// [Host] : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 1. No HwIntrinsics : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 2. SSE : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 3. AVX : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT + +// Runtime=.NET 6.0 + +// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |------------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|------:|------:|------:|----------:| +// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 57.45 ns | 0.126 ns | 0.118 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4 | 2. SSE | COMPlus_EnableAVX=0 | 96 | 14.70 ns | 0.105 ns | 0.098 ns | 0.26 | - | - | - | - | +// | Pad3Shuffle4 | 3. AVX | Empty | 96 | 14.63 ns | 0.070 ns | 0.062 ns | 0.25 | - | - | - | - | +// | | | | | | | | | | | | | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 12.08 ns | 0.028 ns | 0.025 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 96 | 14.04 ns | 0.050 ns | 0.044 ns | 1.16 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 3. AVX | Empty | 96 | 13.90 ns | 0.086 ns | 0.080 ns | 1.15 | - | - | - | - | +// | | | | | | | | | | | | | +// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 202.67 ns | 2.010 ns | 1.678 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4 | 2. SSE | COMPlus_EnableAVX=0 | 384 | 25.54 ns | 0.060 ns | 0.053 ns | 0.13 | - | - | - | - | +// | Pad3Shuffle4 | 3. AVX | Empty | 384 | 25.72 ns | 0.139 ns | 0.130 ns | 0.13 | - | - | - | - | +// | | | | | | | | | | | | | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 60.35 ns | 0.080 ns | 0.071 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 384 | 25.18 ns | 0.388 ns | 0.324 ns | 0.42 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 3. AVX | Empty | 384 | 26.21 ns | 0.067 ns | 0.059 ns | 0.43 | - | - | - | - | +// | | | | | | | | | | | | | +// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 393.88 ns | 1.353 ns | 1.199 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4 | 2. SSE | COMPlus_EnableAVX=0 | 768 | 39.44 ns | 0.230 ns | 0.204 ns | 0.10 | - | - | - | - | +// | Pad3Shuffle4 | 3. AVX | Empty | 768 | 39.51 ns | 0.108 ns | 0.101 ns | 0.10 | - | - | - | - | +// | | | | | | | | | | | | | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 112.02 ns | 0.140 ns | 0.131 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 768 | 38.60 ns | 0.091 ns | 0.080 ns | 0.34 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 3. AVX | Empty | 768 | 38.18 ns | 0.100 ns | 0.084 ns | 0.34 | - | - | - | - | +// | | | | | | | | | | | | | +// | Pad3Shuffle4 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 777.95 ns | 1.719 ns | 1.342 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4 | 2. SSE | COMPlus_EnableAVX=0 | 1536 | 73.11 ns | 0.090 ns | 0.075 ns | 0.09 | - | - | - | - | +// | Pad3Shuffle4 | 3. AVX | Empty | 1536 | 73.41 ns | 0.125 ns | 0.117 ns | 0.09 | - | - | - | - | +// | | | | | | | | | | | | | +// | Pad3Shuffle4FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 218.14 ns | 0.377 ns | 0.334 ns | 1.00 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 1536 | 72.55 ns | 1.418 ns | 1.184 ns | 0.33 | - | - | - | - | +// | Pad3Shuffle4FastFallback | 3. AVX | Empty | 1536 | 73.15 ns | 0.330 ns | 0.292 ns | 0.34 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Bulk/PremultiplyVector4.cs b/tests/ImageSharp.Benchmarks/Bulk/PremultiplyVector4.cs new file mode 100644 index 0000000000..4e2d2d36e9 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Bulk/PremultiplyVector4.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using BenchmarkDotNet.Attributes; + +namespace SixLabors.ImageSharp.Benchmarks.Bulk; + +public class PremultiplyVector4 +{ + private static readonly Vector4[] Vectors = Vector4Factory.CreateVectors(); + + [Benchmark(Baseline = true)] + public void PremultiplyBaseline() + { + ref Vector4 baseRef = ref MemoryMarshal.GetReference(Vectors); + + for (nuint i = 0; i < (uint)Vectors.Length; i++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, i); + Premultiply(ref v); + } + } + + [Benchmark] + public void Premultiply() + { + ref Vector4 baseRef = ref MemoryMarshal.GetReference(Vectors); + + for (nuint i = 0; i < (uint)Vectors.Length; i++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, i); + Numerics.Premultiply(ref v); + } + } + + [Benchmark] + public void PremultiplyBulk() => Numerics.Premultiply(Vectors); + + [MethodImpl(InliningOptions.ShortMethod)] + private static void Premultiply(ref Vector4 source) + { + float w = source.W; + source *= w; + source.W = w; + } +} diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/Rgb24Bytes.cs b/tests/ImageSharp.Benchmarks/Bulk/Rgb24Bytes.cs similarity index 96% rename from tests/ImageSharp.Benchmarks/Color/Bulk/Rgb24Bytes.cs rename to tests/ImageSharp.Benchmarks/Bulk/Rgb24Bytes.cs index 1795ac30bb..8dd2edf008 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/Rgb24Bytes.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/Rgb24Bytes.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; public abstract class Rgb24Bytes where TPixel : unmanaged, IPixel diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle3Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/Shuffle3Channel.cs similarity index 56% rename from tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle3Channel.cs rename to tests/ImageSharp.Benchmarks/Bulk/Shuffle3Channel.cs index 7f6f5901ff..8b7b89eb36 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle3Channel.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/Shuffle3Channel.cs @@ -3,12 +3,12 @@ using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.HwIntrinsics_SSE_AVX))] public class Shuffle3Channel { - private static readonly DefaultShuffle3 Control = new DefaultShuffle3(1, 0, 2); + private static readonly DefaultShuffle3 Control = new(SimdUtils.Shuffle.MMShuffle3102); private byte[] source; private byte[] destination; @@ -25,9 +25,7 @@ public class Shuffle3Channel [Benchmark] public void Shuffle3() - { - SimdUtils.Shuffle3(this.source, this.destination, Control); - } + => SimdUtils.Shuffle3(this.source, this.destination, Control); } // 2020-11-02 @@ -60,3 +58,34 @@ public class Shuffle3Channel // | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 773.70 ns | 5.516 ns | 4.890 ns | 772.96 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle3 | 2. AVX | Empty | 1536 | 190.41 ns | 1.090 ns | 0.851 ns | 190.38 ns | 0.25 | 0.00 | - | - | - | - | // | Shuffle3 | 3. SSE | COMPlus_EnableAVX=0 | 1536 | 190.94 ns | 0.985 ns | 0.769 ns | 190.85 ns | 0.25 | 0.00 | - | - | - | - | + +// 2023-02-21 +// ########## +// +// BenchmarkDotNet=v0.13.0, OS=Windows 10.0.22621 +// 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores +// .NET SDK= 7.0.103 +// [Host] : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 1. No HwIntrinsics : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 2. SSE : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 3. AVX : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT + +// Runtime=.NET 6.0 + +// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |--------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|------:|------:|------:|----------:| +// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 96 | 44.55 ns | 0.564 ns | 0.528 ns | 1.00 | - | - | - | - | +// | Shuffle3 | 2. SSE | COMPlus_EnableAVX=0 | 96 | 15.46 ns | 0.064 ns | 0.060 ns | 0.35 | - | - | - | - | +// | Shuffle3 | 3. AVX | Empty | 96 | 15.18 ns | 0.056 ns | 0.053 ns | 0.34 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 384 | 155.68 ns | 0.539 ns | 0.504 ns | 1.00 | - | - | - | - | +// | Shuffle3 | 2. SSE | COMPlus_EnableAVX=0 | 384 | 30.04 ns | 0.100 ns | 0.089 ns | 0.19 | - | - | - | - | +// | Shuffle3 | 3. AVX | Empty | 384 | 29.70 ns | 0.061 ns | 0.054 ns | 0.19 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 768 | 302.76 ns | 1.023 ns | 0.957 ns | 1.00 | - | - | - | - | +// | Shuffle3 | 2. SSE | COMPlus_EnableAVX=0 | 768 | 50.24 ns | 0.098 ns | 0.092 ns | 0.17 | - | - | - | - | +// | Shuffle3 | 3. AVX | Empty | 768 | 49.28 ns | 0.156 ns | 0.131 ns | 0.16 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1536 | 596.53 ns | 2.675 ns | 2.503 ns | 1.00 | - | - | - | - | +// | Shuffle3 | 2. SSE | COMPlus_EnableAVX=0 | 1536 | 94.09 ns | 0.312 ns | 0.260 ns | 0.16 | - | - | - | - | +// | Shuffle3 | 3. AVX | Empty | 1536 | 93.57 ns | 0.196 ns | 0.183 ns | 0.16 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle4Slice3Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/Shuffle4Slice3Channel.cs similarity index 53% rename from tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle4Slice3Channel.cs rename to tests/ImageSharp.Benchmarks/Bulk/Shuffle4Slice3Channel.cs index 0a0afb7050..5ade55c73f 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/Shuffle4Slice3Channel.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/Shuffle4Slice3Channel.cs @@ -3,13 +3,12 @@ using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.HwIntrinsics_SSE_AVX))] public class Shuffle4Slice3Channel { - private static readonly DefaultShuffle4Slice3 Control = new DefaultShuffle4Slice3(1, 0, 3, 2); - private static readonly XYZWShuffle4Slice3 ControlFast = default; + private static readonly DefaultShuffle4Slice3 Control = new(SimdUtils.Shuffle.MMShuffle1032); private byte[] source; private byte[] destination; @@ -26,15 +25,11 @@ public class Shuffle4Slice3Channel [Benchmark] public void Shuffle4Slice3() - { - SimdUtils.Shuffle4Slice3(this.source, this.destination, Control); - } + => SimdUtils.Shuffle4Slice3(this.source, this.destination, Control); [Benchmark] public void Shuffle4Slice3FastFallback() - { - SimdUtils.Shuffle4Slice3(this.source, this.destination, ControlFast); - } + => SimdUtils.Shuffle4Slice3(this.source, this.destination, default(XYZWShuffle4Slice3)); } // 2020-10-29 @@ -91,3 +86,58 @@ public class Shuffle4Slice3Channel // | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 382.97 ns | 1.064 ns | 0.831 ns | 382.87 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Slice3FastFallback | 2. AVX | Empty | 2048 | 126.93 ns | 0.382 ns | 0.339 ns | 126.94 ns | 0.33 | 0.00 | - | - | - | - | // | Shuffle4Slice3FastFallback | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 149.36 ns | 1.875 ns | 1.754 ns | 149.33 ns | 0.39 | 0.00 | - | - | - | - | + +// 2023-02-21 +// ########## +// +// BenchmarkDotNet=v0.13.0, OS=Windows 10.0.22621 +// 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores +// .NET SDK= 7.0.103 +// [Host] : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 1. No HwIntrinsics : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 2. SSE : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 3. AVX : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// +// Runtime=.NET 6.0 +// +// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |--------------------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|------:|------:|------:|----------:| +// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 45.59 ns | 0.166 ns | 0.147 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 128 | 15.62 ns | 0.056 ns | 0.052 ns | 0.34 | - | - | - | - | +// | Shuffle4Slice3 | 3. AVX | Empty | 128 | 16.37 ns | 0.047 ns | 0.040 ns | 0.36 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 13.23 ns | 0.028 ns | 0.026 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 128 | 14.41 ns | 0.013 ns | 0.012 ns | 1.09 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. AVX | Empty | 128 | 14.70 ns | 0.050 ns | 0.047 ns | 1.11 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 85.48 ns | 0.192 ns | 0.179 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 256 | 19.18 ns | 0.230 ns | 0.204 ns | 0.22 | - | - | - | - | +// | Shuffle4Slice3 | 3. AVX | Empty | 256 | 18.66 ns | 0.017 ns | 0.015 ns | 0.22 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 24.34 ns | 0.078 ns | 0.073 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 256 | 18.58 ns | 0.061 ns | 0.057 ns | 0.76 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. AVX | Empty | 256 | 19.23 ns | 0.018 ns | 0.016 ns | 0.79 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 165.31 ns | 0.742 ns | 0.694 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 512 | 28.10 ns | 0.077 ns | 0.068 ns | 0.17 | - | - | - | - | +// | Shuffle4Slice3 | 3. AVX | Empty | 512 | 28.99 ns | 0.018 ns | 0.014 ns | 0.18 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 53.45 ns | 0.270 ns | 0.226 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 512 | 27.50 ns | 0.034 ns | 0.028 ns | 0.51 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. AVX | Empty | 512 | 28.76 ns | 0.017 ns | 0.015 ns | 0.54 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 323.87 ns | 0.549 ns | 0.487 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 1024 | 40.81 ns | 0.056 ns | 0.050 ns | 0.13 | - | - | - | - | +// | Shuffle4Slice3 | 3. AVX | Empty | 1024 | 39.95 ns | 0.075 ns | 0.067 ns | 0.12 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 101.37 ns | 0.080 ns | 0.067 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 1024 | 40.72 ns | 0.049 ns | 0.041 ns | 0.40 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. AVX | Empty | 1024 | 39.78 ns | 0.029 ns | 0.027 ns | 0.39 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Slice3 | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 642.95 ns | 2.067 ns | 1.933 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3 | 2. SSE | COMPlus_EnableAVX=0 | 2048 | 73.19 ns | 0.082 ns | 0.077 ns | 0.11 | - | - | - | - | +// | Shuffle4Slice3 | 3. AVX | Empty | 2048 | 69.83 ns | 0.319 ns | 0.267 ns | 0.11 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Slice3FastFallback | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 196.85 ns | 0.238 ns | 0.211 ns | 1.00 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 2. SSE | COMPlus_EnableAVX=0 | 2048 | 72.89 ns | 0.117 ns | 0.098 ns | 0.37 | - | - | - | - | +// | Shuffle4Slice3FastFallback | 3. AVX | Empty | 2048 | 69.59 ns | 0.073 ns | 0.061 ns | 0.35 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleByte4Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/ShuffleByte4Channel.cs similarity index 53% rename from tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleByte4Channel.cs rename to tests/ImageSharp.Benchmarks/Bulk/ShuffleByte4Channel.cs index 6323e1c55c..911c4e0a58 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleByte4Channel.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ShuffleByte4Channel.cs @@ -3,7 +3,7 @@ using BenchmarkDotNet.Attributes; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.HwIntrinsics_SSE_AVX))] public class ShuffleByte4Channel @@ -24,9 +24,7 @@ public class ShuffleByte4Channel [Benchmark] public void Shuffle4Channel() - { - SimdUtils.Shuffle4(this.source, this.destination, default); - } + => SimdUtils.Shuffle4(this.source, this.destination, default); } // 2020-10-29 @@ -63,3 +61,38 @@ public class ShuffleByte4Channel // | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 315.29 ns | 5.206 ns | 6.583 ns | 1.00 | 0.00 | - | - | - | - | // | Shuffle4Channel | 2. AVX | Empty | 2048 | 57.37 ns | 1.152 ns | 1.078 ns | 0.18 | 0.01 | - | - | - | - | // | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 65.75 ns | 1.198 ns | 1.600 ns | 0.21 | 0.01 | - | - | - | - | + +// 2023-02-21 +// ########## +// +// BenchmarkDotNet=v0.13.0, OS=Windows 10.0.22621 +// 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores +// .NET SDK= 7.0.103 +// [Host] : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 1. No HwIntrinsics : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 2. SSE : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 3. AVX : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// +// Runtime=.NET 6.0 +// +// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |---------------- |------------------- |-------------------------------------------------- |------ |----------:|---------:|---------:|------:|--------:|------:|------:|------:|----------:| +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 10.76 ns | 0.033 ns | 0.029 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 128 | 11.39 ns | 0.045 ns | 0.040 ns | 1.06 | 0.01 | - | - | - | - | +// | Shuffle4Channel | 3. AVX | Empty | 128 | 14.05 ns | 0.029 ns | 0.024 ns | 1.31 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 32.09 ns | 0.655 ns | 1.000 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 256 | 14.03 ns | 0.047 ns | 0.041 ns | 0.44 | 0.02 | - | - | - | - | +// | Shuffle4Channel | 3. AVX | Empty | 256 | 15.18 ns | 0.052 ns | 0.043 ns | 0.48 | 0.03 | - | - | - | - | +// | | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 59.26 ns | 0.084 ns | 0.070 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 512 | 18.80 ns | 0.036 ns | 0.034 ns | 0.32 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 3. AVX | Empty | 512 | 17.69 ns | 0.038 ns | 0.034 ns | 0.30 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 112.48 ns | 0.285 ns | 0.253 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 1024 | 31.57 ns | 0.041 ns | 0.036 ns | 0.28 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 3. AVX | Empty | 1024 | 28.41 ns | 0.068 ns | 0.064 ns | 0.25 | 0.00 | - | - | - | - | +// | | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 218.59 ns | 0.303 ns | 0.283 ns | 1.00 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 2048 | 53.04 ns | 0.106 ns | 0.099 ns | 0.24 | 0.00 | - | - | - | - | +// | Shuffle4Channel | 3. AVX | Empty | 2048 | 34.74 ns | 0.061 ns | 0.054 ns | 0.16 | 0.00 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleFloat4Channel.cs b/tests/ImageSharp.Benchmarks/Bulk/ShuffleFloat4Channel.cs similarity index 53% rename from tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleFloat4Channel.cs rename to tests/ImageSharp.Benchmarks/Bulk/ShuffleFloat4Channel.cs index bdeb880828..5bb3cf9165 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ShuffleFloat4Channel.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ShuffleFloat4Channel.cs @@ -4,12 +4,11 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Tests; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.HwIntrinsics_SSE_AVX))] public class ShuffleFloat4Channel { - private static readonly byte Control = default(WXYZShuffle4).Control; private float[] source; private float[] destination; @@ -25,9 +24,7 @@ public class ShuffleFloat4Channel [Benchmark] public void Shuffle4Channel() - { - SimdUtils.Shuffle4(this.source, this.destination, Control); - } + => SimdUtils.Shuffle4(this.source, this.destination, SimdUtils.Shuffle.MMShuffle2103); } // 2020-10-29 @@ -64,3 +61,38 @@ public class ShuffleFloat4Channel // | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 980.134 ns | 3.7407 ns | 3.1237 ns | 1.00 | - | - | - | - | // | Shuffle4Channel | 2. AVX | Empty | 2048 | 105.120 ns | 0.6140 ns | 0.5443 ns | 0.11 | - | - | - | - | // | Shuffle4Channel | 3. SSE | COMPlus_EnableAVX=0 | 2048 | 216.473 ns | 2.3268 ns | 2.0627 ns | 0.22 | - | - | - | - | + +// 2023-02-21 +// ########## +// +// BenchmarkDotNet=v0.13.0, OS=Windows 10.0.22621 +// 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores +// .NET SDK= 7.0.103 +// [Host] : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 1. No HwIntrinsics : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 2. SSE : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// 3. AVX : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT +// +// Runtime=.NET 6.0 +// +// | Method | Job | EnvironmentVariables | Count | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |---------------- |------------------- |-------------------------------------------------- |------ |-----------:|----------:|----------:|------:|------:|------:|------:|----------:| +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 128 | 57.819 ns | 0.2360 ns | 0.1970 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 128 | 11.564 ns | 0.0234 ns | 0.0195 ns | 0.20 | - | - | - | - | +// | Shuffle4Channel | 3. AVX | Empty | 128 | 7.770 ns | 0.0696 ns | 0.0617 ns | 0.13 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 256 | 105.282 ns | 0.2713 ns | 0.2405 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 256 | 19.867 ns | 0.0393 ns | 0.0348 ns | 0.19 | - | - | - | - | +// | Shuffle4Channel | 3. AVX | Empty | 256 | 17.586 ns | 0.0582 ns | 0.0544 ns | 0.17 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 512 | 200.799 ns | 0.5678 ns | 0.5033 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 512 | 41.137 ns | 0.1524 ns | 0.1351 ns | 0.20 | - | - | - | - | +// | Shuffle4Channel | 3. AVX | Empty | 512 | 24.040 ns | 0.0445 ns | 0.0395 ns | 0.12 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 1024 | 401.046 ns | 0.5865 ns | 0.5199 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 1024 | 94.904 ns | 0.4633 ns | 0.4334 ns | 0.24 | - | - | - | - | +// | Shuffle4Channel | 3. AVX | Empty | 1024 | 68.456 ns | 0.1192 ns | 0.0996 ns | 0.17 | - | - | - | - | +// | | | | | | | | | | | | | +// | Shuffle4Channel | 1. No HwIntrinsics | COMPlus_EnableHWIntrinsic=0,COMPlus_FeatureSIMD=0 | 2048 | 772.297 ns | 0.6270 ns | 0.5558 ns | 1.00 | - | - | - | - | +// | Shuffle4Channel | 2. SSE | COMPlus_EnableAVX=0 | 2048 | 184.561 ns | 0.4319 ns | 0.4040 ns | 0.24 | - | - | - | - | +// | Shuffle4Channel | 3. AVX | Empty | 2048 | 133.634 ns | 1.7864 ns | 1.8345 ns | 0.17 | - | - | - | - | diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToRgba32Bytes.cs b/tests/ImageSharp.Benchmarks/Bulk/ToRgba32Bytes.cs similarity index 97% rename from tests/ImageSharp.Benchmarks/Color/Bulk/ToRgba32Bytes.cs rename to tests/ImageSharp.Benchmarks/Bulk/ToRgba32Bytes.cs index b6e0696735..6d3f8f9528 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToRgba32Bytes.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToRgba32Bytes.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; public abstract class ToRgba32Bytes where TPixel : unmanaged, IPixel diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4.cs similarity index 96% rename from tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs rename to tests/ImageSharp.Benchmarks/Bulk/ToVector4.cs index 31dfd0be93..3b4360b161 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4.cs @@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; public abstract class ToVector4 where TPixel : unmanaged, IPixel diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs similarity index 98% rename from tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs rename to tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs index b2f12cf9d9..2f1064439b 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Bgra32.cs @@ -6,7 +6,7 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.ShortMultiFramework))] public class ToVector4_Bgra32 : ToVector4 diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgb24.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs similarity index 98% rename from tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgb24.cs rename to tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs index f22868335d..2c700a733f 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgb24.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgb24.cs @@ -6,7 +6,7 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.ShortMultiFramework))] public class ToVector4_Rgb24 : ToVector4 diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs similarity index 95% rename from tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs rename to tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs index 6c28605161..9abf0ed22a 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/ToVector4_Rgba32.cs @@ -9,7 +9,7 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; +namespace SixLabors.ImageSharp.Benchmarks.Bulk; [Config(typeof(Config.ShortCore31))] public class ToVector4_Rgba32 : ToVector4 @@ -54,13 +54,13 @@ public class ToVector4_Rgba32 : ToVector4 Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - int n = dFloats.Length / Vector.Count; + nuint n = (uint)dFloats.Length / (uint)Vector.Count; ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference((ReadOnlySpan)sBytes)); ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dFloats)); ref Vector destBaseU = ref Unsafe.As, Vector>(ref destBase); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { Vector b = Unsafe.Add(ref sourceBase, i); @@ -75,10 +75,10 @@ public class ToVector4_Rgba32 : ToVector4 Unsafe.Add(ref d, 3) = w3; } - n = dFloats.Length / Vector.Count; + n = (uint)(dFloats.Length / Vector.Count); var scale = new Vector(1f / 255f); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { ref Vector dRef = ref Unsafe.Add(ref destBase, i); @@ -96,13 +96,13 @@ public class ToVector4_Rgba32 : ToVector4 Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); - int n = dFloats.Length / Vector.Count; + nuint n = (uint)dFloats.Length / (uint)Vector.Count; ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference((ReadOnlySpan)sBytes)); ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dFloats)); var scale = new Vector(1f / 255f); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { Vector b = Unsafe.Add(ref sourceBase, i); diff --git a/tests/ImageSharp.Benchmarks/Bulk/UnPremultiplyVector4.cs b/tests/ImageSharp.Benchmarks/Bulk/UnPremultiplyVector4.cs new file mode 100644 index 0000000000..95fdd9a8c3 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Bulk/UnPremultiplyVector4.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using BenchmarkDotNet.Attributes; + +namespace SixLabors.ImageSharp.Benchmarks.Bulk; + +public class UnPremultiplyVector4 +{ + private static readonly Vector4[] Vectors = Vector4Factory.CreateVectors(); + + [Benchmark(Baseline = true)] + public void UnPremultiplyBaseline() + { + ref Vector4 baseRef = ref MemoryMarshal.GetReference(Vectors); + + for (nuint i = 0; i < (uint)Vectors.Length; i++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, i); + + UnPremultiply(ref v); + } + } + + [Benchmark] + public void UnPremultiply() + { + ref Vector4 baseRef = ref MemoryMarshal.GetReference(Vectors); + + for (nuint i = 0; i < (uint)Vectors.Length; i++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, i); + Numerics.UnPremultiply(ref v); + } + } + + [Benchmark] + public void UnPremultiplyBulk() => Numerics.UnPremultiply(Vectors); + + [MethodImpl(InliningOptions.ShortMethod)] + private static void UnPremultiply(ref Vector4 source) + { + float w = source.W; + source /= w; + source.W = w; + } +} diff --git a/tests/ImageSharp.Benchmarks/Bulk/Vector4Factory.cs b/tests/ImageSharp.Benchmarks/Bulk/Vector4Factory.cs new file mode 100644 index 0000000000..aa555f5c41 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Bulk/Vector4Factory.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.Benchmarks.Bulk; + +internal static class Vector4Factory +{ + public static Vector4[] CreateVectors(int length = 2048, int min = 0, int max = 1) + { + Random rnd = new(42); + return GenerateRandomVectorArray(rnd, length, min, max); + } + + private static Vector4[] GenerateRandomVectorArray(Random rnd, int length, float minVal, float maxVal) + { + Vector4[] values = new Vector4[length]; + + for (int i = 0; i < length; i++) + { + ref Vector4 v = ref values[i]; + v.X = GetRandomFloat(rnd, minVal, maxVal); + v.Y = GetRandomFloat(rnd, minVal, maxVal); + v.Z = GetRandomFloat(rnd, minVal, maxVal); + v.W = GetRandomFloat(rnd, minVal, maxVal); + } + + return values; + } + + private static float GetRandomFloat(Random rnd, float minVal, float maxVal) + => (float)rnd.NextDouble() * (maxVal - minVal) + minVal; +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs index f36f92e907..14598c747a 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs @@ -72,8 +72,8 @@ public unsafe class Block8x8F_CopyTo1x1 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row) { - ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float)); - ref byte d = ref Unsafe.Add(ref destBase, row * destStride); + ref byte s = ref Unsafe.Add(ref selfBase, (uint)row * 8 * sizeof(float)); + ref byte d = ref Unsafe.Add(ref destBase, (uint)(row * destStride)); Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); } @@ -82,7 +82,7 @@ public unsafe class Block8x8F_CopyTo1x1 { ref Block8x8F s = ref this.block; ref float origin = ref Unsafe.AsRef(this.bufferPtr); - int stride = Width; + nuint stride = (uint)Width; ref Vector d0 = ref Unsafe.As>(ref origin); ref Vector d1 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride)); @@ -117,7 +117,7 @@ public unsafe class Block8x8F_CopyTo1x1 { ref Block8x8F s = ref this.block; ref float origin = ref Unsafe.AsRef(this.bufferPtr); - int stride = Width; + nuint stride = (uint)Width; ref Vector d0 = ref Unsafe.As>(ref origin); ref Vector d1 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride)); @@ -141,29 +141,29 @@ public unsafe class Block8x8F_CopyTo1x1 [Benchmark] public void UseVector8_V3() { - int stride = Width * sizeof(float); + nuint stride = (uint)Width * sizeof(float); ref float d = ref this.unpinnedBuffer[0]; ref Vector s = ref Unsafe.As>(ref this.block); Vector v0 = s; - Vector v1 = Unsafe.AddByteOffset(ref s, (IntPtr)1); - Vector v2 = Unsafe.AddByteOffset(ref s, (IntPtr)2); - Vector v3 = Unsafe.AddByteOffset(ref s, (IntPtr)3); + Vector v1 = Unsafe.AddByteOffset(ref s, 1); + Vector v2 = Unsafe.AddByteOffset(ref s, 2); + Vector v3 = Unsafe.AddByteOffset(ref s, 3); Unsafe.As>(ref d) = v0; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)stride)) = v1; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 2))) = v2; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 3))) = v3; - - v0 = Unsafe.AddByteOffset(ref s, (IntPtr)4); - v1 = Unsafe.AddByteOffset(ref s, (IntPtr)5); - v2 = Unsafe.AddByteOffset(ref s, (IntPtr)6); - v3 = Unsafe.AddByteOffset(ref s, (IntPtr)7); - - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 4))) = v0; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 5))) = v1; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 6))) = v2; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 7))) = v3; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride)) = v1; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride * 2)) = v2; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride * 3)) = v3; + + v0 = Unsafe.AddByteOffset(ref s, 4); + v1 = Unsafe.AddByteOffset(ref s, 5); + v2 = Unsafe.AddByteOffset(ref s, 6); + v3 = Unsafe.AddByteOffset(ref s, 7); + + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride * 4)) = v0; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride * 5)) = v1; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride * 6)) = v2; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride * 7)) = v3; } [Benchmark] @@ -254,7 +254,7 @@ public unsafe class Block8x8F_CopyTo1x1 [Benchmark] public void UseVector256_Avx2_Variant3_RefCast() { - int stride = Width; + nuint stride = (uint)Width; ref float d = ref this.unpinnedBuffer[0]; ref Vector256 s = ref Unsafe.As>(ref this.block); @@ -282,29 +282,29 @@ public unsafe class Block8x8F_CopyTo1x1 [Benchmark] public void UseVector256_Avx2_Variant3_RefCast_Mod() { - int stride = Width * sizeof(float); + nuint stride = (uint)Width * sizeof(float); ref float d = ref this.unpinnedBuffer[0]; ref Vector256 s = ref Unsafe.As>(ref this.block); Vector256 v0 = s; - Vector256 v1 = Unsafe.AddByteOffset(ref s, (IntPtr)1); - Vector256 v2 = Unsafe.AddByteOffset(ref s, (IntPtr)2); - Vector256 v3 = Unsafe.AddByteOffset(ref s, (IntPtr)3); + Vector256 v1 = Unsafe.AddByteOffset(ref s, 1); + Vector256 v2 = Unsafe.AddByteOffset(ref s, 2); + Vector256 v3 = Unsafe.AddByteOffset(ref s, 3); Unsafe.As>(ref d) = v0; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)stride)) = v1; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 2))) = v2; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 3))) = v3; - - v0 = Unsafe.AddByteOffset(ref s, (IntPtr)4); - v1 = Unsafe.AddByteOffset(ref s, (IntPtr)5); - v2 = Unsafe.AddByteOffset(ref s, (IntPtr)6); - v3 = Unsafe.AddByteOffset(ref s, (IntPtr)7); - - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 4))) = v0; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 5))) = v1; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 6))) = v2; - Unsafe.As>(ref Unsafe.AddByteOffset(ref d, (IntPtr)(stride * 7))) = v3; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride)) = v1; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride * 2)) = v2; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride * 3)) = v3; + + v0 = Unsafe.AddByteOffset(ref s, 4); + v1 = Unsafe.AddByteOffset(ref s, 5); + v2 = Unsafe.AddByteOffset(ref s, 6); + v3 = Unsafe.AddByteOffset(ref s, 7); + + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride * 4)) = v0; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride * 5)) = v1; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride * 6)) = v2; + Unsafe.As>(ref Unsafe.AddByteOffset(ref d, stride * 7)) = v3; } // [Benchmark] diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs index 88c0098230..72b6bb72e8 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs @@ -47,9 +47,9 @@ public class Block8x8F_CopyTo2x2 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void WidenCopyImpl2x2(ref Block8x8F src, ref float destBase, int row, int destStride) { - ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * (uint)row); ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); - ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride); + ref float destLocalOrigo = ref Unsafe.Add(ref destBase, (uint)(row * 2 * destStride)); Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X; Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X; @@ -69,23 +69,23 @@ public class Block8x8F_CopyTo2x2 Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W; Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W; - - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride), 0) = selfLeft.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride), 1) = selfLeft.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride), 2) = selfLeft.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride), 3) = selfLeft.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride), 4) = selfLeft.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride), 5) = selfLeft.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride), 6) = selfLeft.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride), 7) = selfLeft.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride + 8), 0) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride + 8), 1) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride + 8), 2) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride + 8), 3) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride + 8), 4) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride + 8), 5) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride + 8), 6) = selfRight.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, (uint)destStride + 8), 7) = selfRight.W; } [Benchmark] @@ -109,9 +109,9 @@ public class Block8x8F_CopyTo2x2 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void WidenCopyImpl2x2_V2(ref Block8x8F src, ref float destBase, int row, int destStride) { - ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * (uint)row); ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); - ref float dest0 = ref Unsafe.Add(ref destBase, row * 2 * destStride); + ref float dest0 = ref Unsafe.Add(ref destBase, (uint)(row * 2 * destStride)); Unsafe.Add(ref dest0, 0) = selfLeft.X; Unsafe.Add(ref dest0, 1) = selfLeft.X; @@ -133,7 +133,7 @@ public class Block8x8F_CopyTo2x2 Unsafe.Add(ref dest1, 6) = selfRight.W; Unsafe.Add(ref dest1, 7) = selfRight.W; - ref float dest2 = ref Unsafe.Add(ref dest0, destStride); + ref float dest2 = ref Unsafe.Add(ref dest0, (uint)destStride); Unsafe.Add(ref dest2, 0) = selfLeft.X; Unsafe.Add(ref dest2, 1) = selfLeft.X; @@ -177,12 +177,12 @@ public class Block8x8F_CopyTo2x2 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void WidenCopyImpl2x2_Vector2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) { - ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * (uint)row); ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); - ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); + ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, (uint)(2 * row * destStride)); ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4); - ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); + ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, (uint)destStride); ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); var xLeft = new Vector2(sLeft.X); @@ -237,12 +237,12 @@ public class Block8x8F_CopyTo2x2 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void WidenCopyImpl2x2_Vector4(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) { - ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * (uint)row); ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); - ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); + ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, (uint)(2 * row * destStride)); ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4); - ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); + ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, (uint)destStride); ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); var xLeft = new Vector4(sLeft.X); @@ -297,11 +297,11 @@ public class Block8x8F_CopyTo2x2 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void WidenCopyImpl2x2_Vector4_SafeRightCorner(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) { - ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * (uint)row); ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); - ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); - ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); + ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, (uint)(2 * row * destStride)); + ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, (uint)destStride); var xLeft = new Vector4(sLeft.X); var yLeft = new Vector4(sLeft.Y); @@ -355,12 +355,12 @@ public class Block8x8F_CopyTo2x2 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void WidenCopyImpl2x2_Vector4_V2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) { - ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * (uint)row); ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); int offset = 2 * row * destStride; - ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset)); - ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride)); + ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, (uint)offset)); + ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, (uint)(offset + destStride))); var xyLeft = new Vector4(sLeft.X) { diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs index a5abeb3b66..722b095870 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs @@ -29,8 +29,6 @@ public class Block8x8F_MultiplyInPlaceBlock } } - var source = default(Block8x8F); - source.LoadFrom(result); - return source; + return Block8x8F.Load(result); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs index afe9d94ae8..8a520b22d3 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs @@ -54,7 +54,7 @@ public unsafe class Block8x8F_Round { ref float b = ref Unsafe.As(ref this.block); - for (int i = 0; i < Block8x8F.Size; i++) + for (nuint i = 0; i < Block8x8F.Size; i++) { ref float v = ref Unsafe.Add(ref b, i); v = (float)Math.Round(v); @@ -178,7 +178,7 @@ public unsafe class Block8x8F_Round { ref Vector128 p = ref Unsafe.As>(ref this.block); p = Sse41.RoundToNearestInteger(p); - var offset = (IntPtr)sizeof(Vector128); + nuint offset = (uint)sizeof(Vector128); p = Sse41.RoundToNearestInteger(p); p = ref Unsafe.AddByteOffset(ref p, offset); @@ -218,7 +218,7 @@ public unsafe class Block8x8F_Round { ref Vector128 p = ref Unsafe.As>(ref this.block); p = Sse41.RoundToNearestInteger(p); - var offset = (IntPtr)sizeof(Vector128); + nuint offset = (uint)sizeof(Vector128); for (int i = 0; i < 15; i++) { @@ -231,7 +231,7 @@ public unsafe class Block8x8F_Round public unsafe void Sse41_V4() { ref Vector128 p = ref Unsafe.As>(ref this.block); - var offset = (IntPtr)sizeof(Vector128); + nuint offset = (uint)sizeof(Vector128); ref Vector128 a = ref p; ref Vector128 b = ref Unsafe.AddByteOffset(ref a, offset); diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs index 6ad20ce679..51cd02bc7a 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/CmykColorConversion.cs @@ -37,4 +37,12 @@ public class CmykColorConversion : ColorConversionBenchmark new JpegColorConverterBase.CmykAvx(8).ConvertToRgbInplace(values); } + + [Benchmark] + public void SimdVectorArm64() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.CmykArm64(8).ConvertToRgbInplace(values); + } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs index 47aac3464e..8bf26d721d 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/GrayscaleColorConversion.cs @@ -29,4 +29,12 @@ public class GrayscaleColorConversion : ColorConversionBenchmark new JpegColorConverterBase.GrayscaleAvx(8).ConvertToRgbInplace(values); } + + [Benchmark] + public void SimdVectorArm() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.GrayscaleArm(8).ConvertToRgbInplace(values); + } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs index 18b3eac611..ba09644219 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/RgbColorConversion.cs @@ -37,4 +37,12 @@ public class RgbColorConversion : ColorConversionBenchmark new JpegColorConverterBase.RgbAvx(8).ConvertToRgbInplace(values); } + + [Benchmark] + public void SimdVectorArm() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.RgbArm(8).ConvertToRgbInplace(values); + } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs index 800190d2d7..87e1bf5aa9 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YCbCrColorConversion.cs @@ -37,4 +37,12 @@ public class YCbCrColorConversion : ColorConversionBenchmark new JpegColorConverterBase.YCbCrAvx(8).ConvertToRgbInplace(values); } + + [Benchmark] + public void SimdVectorArm() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.YCbCrArm(8).ConvertToRgbInplace(values); + } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs index 991d3b0d02..136182936f 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/ColorConversion/YccKColorConverter.cs @@ -37,4 +37,12 @@ public class YccKColorConverter : ColorConversionBenchmark new JpegColorConverterBase.YccKAvx(8).ConvertToRgbInplace(values); } + + [Benchmark] + public void SimdVectorArm64() + { + var values = new JpegColorConverterBase.ComponentValues(this.Input, 0); + + new JpegColorConverterBase.YccKArm64(8).ConvertToRgbInplace(values); + } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PremultiplyVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PremultiplyVector4.cs deleted file mode 100644 index d30693c6c7..0000000000 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PremultiplyVector4.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using BenchmarkDotNet.Attributes; - -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; - -[Config(typeof(Config.ShortCore31))] -public class PremultiplyVector4 -{ - private static readonly Vector4[] Vectors = CreateVectors(); - - [Benchmark(Baseline = true)] - public void PremultiplyBaseline() - { - ref Vector4 baseRef = ref MemoryMarshal.GetReference(Vectors); - - for (int i = 0; i < Vectors.Length; i++) - { - ref Vector4 v = ref Unsafe.Add(ref baseRef, i); - Premultiply(ref v); - } - } - - [Benchmark] - public void Premultiply() - { - Numerics.Premultiply(Vectors); - } - - [MethodImpl(InliningOptions.ShortMethod)] - private static void Premultiply(ref Vector4 source) - { - float w = source.W; - source *= w; - source.W = w; - } - - private static Vector4[] CreateVectors() - { - var rnd = new Random(42); - return GenerateRandomVectorArray(rnd, 2048, 0, 1); - } - - private static Vector4[] GenerateRandomVectorArray(Random rnd, int length, float minVal, float maxVal) - { - var values = new Vector4[length]; - - for (int i = 0; i < length; i++) - { - ref Vector4 v = ref values[i]; - v.X = GetRandomFloat(rnd, minVal, maxVal); - v.Y = GetRandomFloat(rnd, minVal, maxVal); - v.Z = GetRandomFloat(rnd, minVal, maxVal); - v.W = GetRandomFloat(rnd, minVal, maxVal); - } - - return values; - } - - private static float GetRandomFloat(Random rnd, float minVal, float maxVal) - => ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; -} diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/UnPremultiplyVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/UnPremultiplyVector4.cs deleted file mode 100644 index ea36a341fc..0000000000 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/UnPremultiplyVector4.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using BenchmarkDotNet.Attributes; - -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk; - -[Config(typeof(Config.ShortCore31))] -public class UnPremultiplyVector4 -{ - private static readonly Vector4[] Vectors = CreateVectors(); - - [Benchmark(Baseline = true)] - public void UnPremultiplyBaseline() - { - ref Vector4 baseRef = ref MemoryMarshal.GetReference(Vectors); - - for (int i = 0; i < Vectors.Length; i++) - { - ref Vector4 v = ref Unsafe.Add(ref baseRef, i); - UnPremultiply(ref v); - } - } - - [Benchmark] - public void UnPremultiply() - { - Numerics.UnPremultiply(Vectors); - } - - [MethodImpl(InliningOptions.ShortMethod)] - private static void UnPremultiply(ref Vector4 source) - { - float w = source.W; - source /= w; - source.W = w; - } - - private static Vector4[] CreateVectors() - { - var rnd = new Random(42); - return GenerateRandomVectorArray(rnd, 2048, 0, 1); - } - - private static Vector4[] GenerateRandomVectorArray(Random rnd, int length, float minVal, float maxVal) - { - var values = new Vector4[length]; - - for (int i = 0; i < length; i++) - { - ref Vector4 v = ref values[i]; - v.X = GetRandomFloat(rnd, minVal, maxVal); - v.Y = GetRandomFloat(rnd, minVal, maxVal); - v.Z = GetRandomFloat(rnd, minVal, maxVal); - v.W = GetRandomFloat(rnd, minVal, maxVal); - } - - return values; - } - - private static float GetRandomFloat(Random rnd, float minVal, float maxVal) - => ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; -} diff --git a/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs b/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs index 50a628ee3c..a6aac20c3b 100644 --- a/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs +++ b/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs @@ -12,7 +12,7 @@ public class GetSetPixel [Benchmark(Baseline = true, Description = "System.Drawing GetSet pixel")] public System.Drawing.Color GetSetSystemDrawing() { - using var source = new Bitmap(400, 400); + using Bitmap source = new(400, 400); source.SetPixel(200, 200, System.Drawing.Color.White); return source.GetPixel(200, 200); } @@ -20,7 +20,7 @@ public class GetSetPixel [Benchmark(Description = "ImageSharp GetSet pixel")] public Rgba32 GetSetImageSharp() { - using var image = new Image(400, 400); + using Image image = new(400, 400); image[200, 200] = Color.White; return image[200, 200]; } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs index d418c45fe3..8d16849825 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs @@ -34,7 +34,7 @@ public abstract class PixelConversion_ConvertFromRgba32 ref T destBaseRef = ref this.Dest[0]; ref Rgba32 sourceBaseRef = ref this.Source[0]; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { Unsafe.Add(ref destBaseRef, i).FromRgba32(ref Unsafe.Add(ref sourceBaseRef, i)); } @@ -48,7 +48,7 @@ public abstract class PixelConversion_ConvertFromRgba32 ref T destBaseRef = ref this.Dest[0]; ref Rgba32 sourceBaseRef = ref this.Source[0]; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { Unsafe.Add(ref destBaseRef, i).FromRgba32(Unsafe.Add(ref sourceBaseRef, i)); } @@ -62,7 +62,7 @@ public abstract class PixelConversion_ConvertFromRgba32 ref T destBaseRef = ref this.Dest[0]; ref Rgba32 sourceBaseRef = ref this.Source[0]; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgba32 s = ref Unsafe.Add(ref sourceBaseRef, i); Unsafe.Add(ref destBaseRef, i).FromBytes(s.R, s.G, s.B, s.A); @@ -111,7 +111,7 @@ public class PixelConversion_ConvertFromRgba32_Compatible : PixelConversion_Conv ref Rgba32 sBase = ref this.CompatibleMemLayoutRunner.Source[0]; ref Rgba32 dBase = ref Unsafe.As(ref this.CompatibleMemLayoutRunner.Dest[0]); - for (int i = 0; i < this.Count; i++) + for (nuint i = 0; i < (uint)this.Count; i++) { Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, i); } @@ -151,7 +151,7 @@ public class PixelConversion_ConvertFromRgba32_Permuted_RgbaToArgb : PixelConver ref Rgba32 sBase = ref this.PermutedRunnerRgbaToArgb.Source[0]; ref TestArgb dBase = ref this.PermutedRunnerRgbaToArgb.Dest[0]; - for (int i = 0; i < this.Count; i++) + for (nuint i = 0; i < (uint)this.Count; i++) { Rgba32 s = Unsafe.Add(ref sBase, i); ref TestArgb d = ref Unsafe.Add(ref dBase, i); diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs index a167ade12b..dd85d06417 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs @@ -71,7 +71,7 @@ public class PixelConversion_ConvertFromVector4 ref T destBaseRef = ref this.dest[0]; ref Vector4 sourceBaseRef = ref this.source[0]; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { Unsafe.Add(ref destBaseRef, i).FromVector4(ref Unsafe.Add(ref sourceBaseRef, i)); } @@ -85,7 +85,7 @@ public class PixelConversion_ConvertFromVector4 ref T destBaseRef = ref this.dest[0]; ref Vector4 sourceBaseRef = ref this.source[0]; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { Unsafe.Add(ref destBaseRef, i).FromVector4(Unsafe.Add(ref sourceBaseRef, i)); } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs index cd8307614e..1a03a0c04f 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs @@ -38,7 +38,7 @@ public class PixelConversion_ConvertToRgba32 ref T sourceBaseRef = ref this.source[0]; ref Rgba32 destBaseRef = ref this.dest[0]; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { Unsafe.Add(ref destBaseRef, i) = Unsafe.Add(ref sourceBaseRef, i).ToRgba32(); } @@ -52,7 +52,7 @@ public class PixelConversion_ConvertToRgba32 ref T sourceBaseRef = ref this.source[0]; ref Rgba32 destBaseRef = ref this.dest[0]; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { Unsafe.Add(ref sourceBaseRef, i).CopyToRgba32(ref Unsafe.Add(ref destBaseRef, i)); } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs index 9ea0c04c77..69a71734a3 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs @@ -33,7 +33,7 @@ public class PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation Rgba32 temp; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { temp = Unsafe.Add(ref sourceBaseRef, i).ToRgba32(); @@ -54,7 +54,7 @@ public class PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation Rgba32 temp = default; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { Unsafe.Add(ref sourceBaseRef, i).CopyToRgba32(ref temp); diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs index c03560e106..9b498b0f2e 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs @@ -30,7 +30,7 @@ public class PixelConversion_ConvertToVector4 ref T sourceBaseRef = ref this.source[0]; ref Vector4 destBaseRef = ref this.dest[0]; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { Unsafe.Add(ref destBaseRef, i) = Unsafe.Add(ref sourceBaseRef, i).ToVector4(); } @@ -44,7 +44,7 @@ public class PixelConversion_ConvertToVector4 ref T sourceBaseRef = ref this.source[0]; ref Vector4 destBaseRef = ref this.dest[0]; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { Unsafe.Add(ref sourceBaseRef, i).CopyToVector4(ref Unsafe.Add(ref destBaseRef, i)); } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs index 462bbbf7b3..50c3d0d131 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs @@ -32,7 +32,7 @@ public class PixelConversion_ConvertToVector4_AsPartOfCompositeOperation Vector4 temp; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { temp = Unsafe.Add(ref sourceBaseRef, i).ToVector4(); @@ -53,7 +53,7 @@ public class PixelConversion_ConvertToVector4_AsPartOfCompositeOperation Vector4 temp = default; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { Unsafe.Add(ref sourceBaseRef, i).CopyToVector4(ref temp); diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs index 86ac928af9..a42c6c253c 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_PackFromRgbPlanes.cs @@ -92,7 +92,7 @@ public unsafe class PixelConversion_PackFromRgbPlanes ref byte b = ref this.rBuf[0]; ref Rgb24 rgb = ref this.rgbBuf[0]; - for (int i = 0; i < this.Count; i++) + for (nuint i = 0; i < (uint)this.Count; i++) { ref Rgb24 d = ref Unsafe.Add(ref rgb, i); d.R = Unsafe.Add(ref r, i); @@ -110,7 +110,7 @@ public unsafe class PixelConversion_PackFromRgbPlanes ref Rgb24 rgb = ref this.rgbBuf[0]; int count = this.Count / 8; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb24 d0 = ref Unsafe.Add(ref rgb, i * 8); ref Rgb24 d1 = ref Unsafe.Add(ref d0, 1); @@ -168,7 +168,7 @@ public unsafe class PixelConversion_PackFromRgbPlanes ref Rgb24 rgb = ref this.rgbBuf[0]; int count = this.Count / 4; - for (int i = 0; i < count; i++) + for (nuint i = 0; i < (uint)count; i++) { ref Rgb24 d0 = ref Unsafe.Add(ref rgb, i * 4); ref Rgb24 d1 = ref Unsafe.Add(ref d0, 1); @@ -205,14 +205,13 @@ public unsafe class PixelConversion_PackFromRgbPlanes ref Vector256 bBase = ref Unsafe.As>(ref this.bFloat[0]); ref Vector256 resultBase = ref Unsafe.As>(ref this.rgbaFloat[0]); - int count = this.Count / Vector256.Count; + nuint count = (uint)this.Count / (uint)Vector256.Count; - ref byte control = ref MemoryMarshal.GetReference(SimdUtils.HwIntrinsics.PermuteMaskEvenOdd8x32); - Vector256 vcontrol = Unsafe.As>(ref control); + Vector256 vcontrol = SimdUtils.HwIntrinsics.PermuteMaskEvenOdd8x32().AsInt32(); var va = Vector256.Create(1F); - for (int i = 0; i < count; i++) + for (nuint i = 0; i < count; i++) { Vector256 r = Unsafe.Add(ref rBase, i); Vector256 g = Unsafe.Add(ref gBase, i); diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs index 9ae36cc8b7..0ee507832a 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs @@ -31,7 +31,7 @@ public class PixelConversion_Rgba32_To_Argb32 ref Rgba32 sBase = ref this.source[0]; ref Argb32 dBase = ref this.dest[0]; - for (int i = 0; i < this.Count; i++) + for (nuint i = 0; i < (uint)this.Count; i++) { Rgba32 s = Unsafe.Add(ref sBase, i); Unsafe.Add(ref dBase, i).FromRgba32(s); @@ -45,7 +45,7 @@ public class PixelConversion_Rgba32_To_Argb32 ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); ref TPixel dBase = ref MemoryMarshal.GetReference(dest); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { Rgba32 s = Unsafe.Add(ref sBase, i); Unsafe.Add(ref dBase, i).FromRgba32(s); @@ -64,7 +64,7 @@ public class PixelConversion_Rgba32_To_Argb32 ref Rgba32 sBase = ref this.source[0]; ref Argb32 dBase = ref this.dest[0]; - for (int i = 0; i < this.Count; i += 2) + for (nuint i = 0; i < (uint)this.Count; i += 2) { ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); Rgba32 s1 = Unsafe.Add(ref s0, 1); @@ -81,7 +81,7 @@ public class PixelConversion_Rgba32_To_Argb32 ref Rgba32 sBase = ref this.source[0]; ref Argb32 dBase = ref this.dest[0]; - for (int i = 0; i < this.Count; i += 4) + for (nuint i = 0; i < (uint)this.Count; i += 4) { ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); @@ -105,7 +105,7 @@ public class PixelConversion_Rgba32_To_Argb32 ref uint sBase = ref Unsafe.As(ref this.source[0]); ref uint dBase = ref Unsafe.As(ref this.dest[0]); - for (int i = 0; i < this.Count; i++) + for (nuint i = 0; i < (uint)this.Count; i++) { uint s = Unsafe.Add(ref sBase, i); Unsafe.Add(ref dBase, i) = FromRgba32.ToArgb32(s); @@ -118,7 +118,7 @@ public class PixelConversion_Rgba32_To_Argb32 ref ulong sBase = ref Unsafe.As(ref this.source[0]); ref ulong dBase = ref Unsafe.As(ref this.dest[0]); - for (int i = 0; i < this.Count / 2; i++) + for (nuint i = 0; i < (uint)this.Count / 2; i++) { ulong s = Unsafe.Add(ref sBase, i); uint lo = (uint)s; diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs index b7b9392379..499f3483dc 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs @@ -52,7 +52,7 @@ public class PixelConversion_Rgba32_To_Bgra32 ref Rgba32 sBase = ref this.source[0]; ref Bgra32 dBase = ref this.dest[0]; - for (int i = 0; i < this.Count; i++) + for (nuint i = 0; i < (uint)this.Count; i++) { ref Rgba32 s = ref Unsafe.Add(ref sBase, i); Unsafe.Add(ref dBase, i).FromRgba32(s); @@ -66,7 +66,7 @@ public class PixelConversion_Rgba32_To_Bgra32 ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); ref TPixel dBase = ref MemoryMarshal.GetReference(dest); - for (int i = 0; i < source.Length; i++) + for (nuint i = 0; i < (uint)source.Length; i++) { ref Rgba32 s = ref Unsafe.Add(ref sBase, i); Unsafe.Add(ref dBase, i).FromRgba32(s); @@ -85,7 +85,7 @@ public class PixelConversion_Rgba32_To_Bgra32 ref Rgba32 sBase = ref this.source[0]; ref Bgra32 dBase = ref this.dest[0]; - for (int i = 0; i < this.Count; i += 2) + for (nuint i = 0; i < (uint)this.Count; i += 2) { ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); Rgba32 s1 = Unsafe.Add(ref s0, 1); @@ -102,7 +102,7 @@ public class PixelConversion_Rgba32_To_Bgra32 ref Rgba32 sBase = ref this.source[0]; ref Bgra32 dBase = ref this.dest[0]; - for (int i = 0; i < this.Count; i += 4) + for (nuint i = 0; i < (uint)this.Count; i += 4) { ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); @@ -127,7 +127,7 @@ public class PixelConversion_Rgba32_To_Bgra32 ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); ref TPixel dBase = ref MemoryMarshal.GetReference(dest); - for (int i = 0; i < source.Length; i += 4) + for (nuint i = 0; i < (uint)source.Length; i += 4) { ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); @@ -157,7 +157,7 @@ public class PixelConversion_Rgba32_To_Bgra32 ref Rgba32 sBase = ref this.source[0]; ref Bgra32 dBase = ref this.dest[0]; - for (int i = 0; i < this.Count / 4; i += 4) + for (nuint i = 0; i < (uint)this.Count / 4; i += 4) { ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); @@ -196,7 +196,7 @@ public class PixelConversion_Rgba32_To_Bgra32 ref uint sBase = ref Unsafe.As(ref this.source[0]); ref uint dBase = ref Unsafe.As(ref this.dest[0]); - for (int i = 0; i < this.Count; i++) + for (nuint i = 0; i < (uint)this.Count; i++) { uint s = Unsafe.Add(ref sBase, i); Unsafe.Add(ref dBase, i) = FromRgba32.ToBgra32(s); @@ -209,7 +209,7 @@ public class PixelConversion_Rgba32_To_Bgra32 ref Tuple4OfUInt32 sBase = ref Unsafe.As(ref this.source[0]); ref Tuple4OfUInt32 dBase = ref Unsafe.As(ref this.dest[0]); - for (int i = 0; i < this.Count / 4; i++) + for (nuint i = 0; i < (uint)this.Count / 4; i++) { ref Tuple4OfUInt32 d = ref Unsafe.Add(ref dBase, i); d = Unsafe.Add(ref sBase, i); @@ -222,7 +222,7 @@ public class PixelConversion_Rgba32_To_Bgra32 { ref Tuple4OfUInt32 sBase = ref Unsafe.As(ref this.source[0]); - for (int i = 0; i < this.Count / 4; i++) + for (nuint i = 0; i < (uint)this.Count / 4; i++) { Unsafe.Add(ref sBase, i).ConvertMe(); } @@ -234,7 +234,7 @@ public class PixelConversion_Rgba32_To_Bgra32 ref Octet sBase = ref Unsafe.As>(ref this.source[0]); ref Octet dBase = ref Unsafe.As>(ref this.dest[0]); - for (int i = 0; i < this.Count / 8; i++) + for (nuint i = 0; i < (uint)this.Count / 8; i++) { BitopsSimdImpl(ref Unsafe.Add(ref sBase, i), ref Unsafe.Add(ref dBase, i)); } @@ -289,7 +289,7 @@ public class PixelConversion_Rgba32_To_Bgra32 ref uint sBase = ref Unsafe.As(ref this.source[0]); ref uint dBase = ref Unsafe.As(ref this.dest[0]); - for (int i = 0; i < this.Count; i++) + for (nuint i = 0; i < (uint)this.Count; i++) { ref uint s0 = ref Unsafe.Add(ref sBase, i); uint s1 = Unsafe.Add(ref s0, 1); @@ -306,7 +306,7 @@ public class PixelConversion_Rgba32_To_Bgra32 ref ulong sBase = ref Unsafe.As(ref this.source[0]); ref ulong dBase = ref Unsafe.As(ref this.dest[0]); - for (int i = 0; i < this.Count / 2; i++) + for (nuint i = 0; i < (uint)this.Count / 2; i++) { ulong s = Unsafe.Add(ref sBase, i); uint lo = (uint)s; @@ -326,7 +326,7 @@ public class PixelConversion_Rgba32_To_Bgra32 ref ulong sBase = ref Unsafe.As(ref this.source[0]); ref ulong dBase = ref Unsafe.As(ref this.dest[0]); - for (int i = 0; i < this.Count / 2; i++) + for (nuint i = 0; i < (uint)this.Count / 2; i++) { ulong s = Unsafe.Add(ref sBase, i); uint lo = (uint)s; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs index 63d363c688..fc4dc1fa16 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs @@ -12,7 +12,7 @@ public class UInt32ToSingle { private float[] data; - private const int Count = 32; + private const uint Count = 32; [GlobalSetup] public void Setup() @@ -25,14 +25,14 @@ public class UInt32ToSingle { ref Vector b = ref Unsafe.As>(ref this.data[0]); - int n = Count / Vector.Count; + nuint n = Count / (uint)Vector.Count; var bVec = new Vector(256.0f / 255.0f); var magicFloat = new Vector(32768.0f); var magicInt = new Vector(1191182336); // reinterpreted value of 32768.0f var mask = new Vector(255); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { ref Vector df = ref Unsafe.Add(ref b, i); @@ -50,14 +50,14 @@ public class UInt32ToSingle [Benchmark] public void StandardSimd() { - int n = Count / Vector.Count; + nuint n = Count / (uint)Vector.Count; ref Vector bf = ref Unsafe.As>(ref this.data[0]); ref Vector bu = ref Unsafe.As, Vector>(ref bf); var scale = new Vector(1f / 255f); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { Vector u = Unsafe.Add(ref bu, i); Vector v = Vector.ConvertToSingle(u); @@ -69,14 +69,14 @@ public class UInt32ToSingle [Benchmark] public void StandardSimdFromInt() { - int n = Count / Vector.Count; + nuint n = Count / (uint)Vector.Count; ref Vector bf = ref Unsafe.As>(ref this.data[0]); ref Vector bu = ref Unsafe.As, Vector>(ref bf); var scale = new Vector(1f / 255f); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { Vector u = Unsafe.Add(ref bu, i); Vector v = Vector.ConvertToSingle(u); @@ -88,12 +88,12 @@ public class UInt32ToSingle [Benchmark] public void StandardSimdFromInt_RefCast() { - int n = Count / Vector.Count; + nuint n = Count / (uint)Vector.Count; ref Vector bf = ref Unsafe.As>(ref this.data[0]); var scale = new Vector(1f / 255f); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { ref Vector fRef = ref Unsafe.Add(ref bf, i); diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs index 07ace06686..5d20f29d18 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs @@ -63,14 +63,14 @@ public class VectorFetching var v = new Vector(this.testValue); ref Vector start = ref Unsafe.As>(ref this.data[0]); - int n = this.InputSize / Vector.Count; + nuint n = (uint)this.InputSize / (uint)Vector.Count; - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { ref Vector p = ref Unsafe.Add(ref start, i); Vector a = p; - a = a * v; + a *= v; p = a; } @@ -82,12 +82,12 @@ public class VectorFetching var v = new Vector(this.testValue); ref Vector start = ref Unsafe.As>(ref this.data[0]); - int n = this.InputSize / Vector.Count; + nuint n = (uint)this.InputSize / (uint)Vector.Count; - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { ref Vector a = ref Unsafe.Add(ref start, i); - a = a * v; + a *= v; } } @@ -100,12 +100,12 @@ public class VectorFetching ref Vector start = ref Unsafe.As>(ref MemoryMarshal.GetReference(span)); - int n = this.InputSize / Vector.Count; + nuint n = (uint)this.InputSize / (uint)Vector.Count; - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { ref Vector a = ref Unsafe.Add(ref start, i); - a = a * v; + a *= v; } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs index 429475ffd3..f391e42012 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs @@ -33,7 +33,7 @@ public class WidenBytesToUInt32 ref Octet sBase = ref Unsafe.As>(ref this.source[0]); ref Octet dBase = ref Unsafe.As>(ref this.dest[0]); - for (int i = 0; i < N; i++) + for (nuint i = 0; i < N; i++) { Unsafe.Add(ref dBase, i).LoadFrom(ref Unsafe.Add(ref sBase, i)); } @@ -42,12 +42,12 @@ public class WidenBytesToUInt32 [Benchmark] public void Simd() { - int n = Count / Vector.Count; + nuint n = Count / (uint)Vector.Count; ref Vector sBase = ref Unsafe.As>(ref this.source[0]); ref Vector dBase = ref Unsafe.As>(ref this.dest[0]); - for (int i = 0; i < n; i++) + for (nuint i = 0; i < n; i++) { Vector b = Unsafe.Add(ref sBase, i); diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 24f618d11b..0ba2f4b949 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -11,7 +11,13 @@ false Debug;Release - + + + + + + + CA1822 @@ -53,7 +59,7 @@ - + diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index 68956c880f..a308c0c468 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Benchmarks; public class PorterDuffBulkVsPixel { - private Configuration Configuration => Configuration.Default; + private static Configuration Configuration => Configuration.Default; - private void BulkVectorConvert( + private static void BulkVectorConvert( Span destination, Span background, Span source, @@ -31,18 +31,18 @@ public class PorterDuffBulkVsPixel Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - PixelOperations.Instance.ToVector4(this.Configuration, background, backgroundSpan); - PixelOperations.Instance.ToVector4(this.Configuration, source, sourceSpan); + PixelOperations.Instance.ToVector4(Configuration, background, backgroundSpan); + PixelOperations.Instance.ToVector4(Configuration, source, sourceSpan); for (int i = 0; i < destination.Length; i++) { destinationSpan[i] = PorterDuffFunctions.NormalSrcOver(backgroundSpan[i], sourceSpan[i], amount[i]); } - PixelOperations.Instance.FromVector4Destructive(this.Configuration, destinationSpan, destination); + PixelOperations.Instance.FromVector4Destructive(Configuration, destinationSpan, destination); } - private void BulkPixelConvert( + private static void BulkPixelConvert( Span destination, Span background, Span source, @@ -62,7 +62,7 @@ public class PorterDuffBulkVsPixel [Benchmark(Description = "ImageSharp BulkVectorConvert")] public Size BulkVectorConvert() { - using var image = new Image(800, 800); + using Image image = new(800, 800); using IMemoryOwner amounts = Configuration.Default.MemoryAllocator.Allocate(image.Width); amounts.GetSpan().Fill(1); @@ -70,7 +70,7 @@ public class PorterDuffBulkVsPixel for (int y = 0; y < image.Height; y++) { Span span = pixels.DangerousGetRowSpan(y); - this.BulkVectorConvert(span, span, span, amounts.GetSpan()); + BulkVectorConvert(span, span, span, amounts.GetSpan()); } return new Size(image.Width, image.Height); @@ -79,14 +79,14 @@ public class PorterDuffBulkVsPixel [Benchmark(Description = "ImageSharp BulkPixelConvert")] public Size BulkPixelConvert() { - using var image = new Image(800, 800); + using Image image = new(800, 800); using IMemoryOwner amounts = Configuration.Default.MemoryAllocator.Allocate(image.Width); amounts.GetSpan().Fill(1); Buffer2D pixels = image.GetRootFramePixelBuffer(); for (int y = 0; y < image.Height; y++) { Span span = pixels.DangerousGetRowSpan(y); - this.BulkPixelConvert(span, span, span, amounts.GetSpan()); + BulkPixelConvert(span, span, span, amounts.GetSpan()); } return new Size(image.Width, image.Height); diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsSingleVector.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsSingleVector.cs new file mode 100644 index 0000000000..ecf8b125f7 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsSingleVector.cs @@ -0,0 +1,68 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +namespace SixLabors.ImageSharp.Benchmarks.PixelBlenders; + +public class PorterDuffBulkVsSingleVector +{ + private Vector4[] backdrop; + private Vector4[] source; + + [GlobalSetup] + public void Setup() + { + this.backdrop = new Vector4[8 * 20]; + this.source = new Vector4[8 * 20]; + + FillRandom(this.backdrop); + FillRandom(this.source); + } + + private static void FillRandom(Vector4[] arr) + { + Random rng = new(); + for (int i = 0; i < arr.Length; i++) + { + arr[i].X = rng.NextSingle(); + arr[i].Y = rng.NextSingle(); + arr[i].Z = rng.NextSingle(); + arr[i].W = rng.NextSingle(); + } + } + + [Benchmark(Description = "Scalar", Baseline = true)] + public Vector4 OverlayValueFunction_Scalar() + { + Vector4 result = default; + for (int i = 0; i < this.backdrop.Length; i++) + { + result = PorterDuffFunctions.NormalSrcOver(this.backdrop[i], this.source[i], .5F); + } + + return result; + } + + [Benchmark(Description = "Avx")] + public Vector256 OverlayValueFunction_Avx() + { + ref Vector256 backdrop = ref Unsafe.As>(ref MemoryMarshal.GetReference(this.backdrop)); + ref Vector256 source = ref Unsafe.As>(ref MemoryMarshal.GetReference(this.source)); + + Vector256 result = default; + Vector256 opacity = Vector256.Create(.5F); + int count = this.backdrop.Length / 2; + for (nuint i = 0; i < (uint)count; i++) + { + result = PorterDuffFunctions.NormalSrcOver(Unsafe.Add(ref backdrop, i), Unsafe.Add(ref source, i), opacity); + } + + return result; + } +} diff --git a/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs b/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs index 2bfc437582..441500c88c 100644 --- a/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs +++ b/tests/ImageSharp.Benchmarks/Processing/BokehBlur.cs @@ -13,7 +13,7 @@ public class BokehBlur [Benchmark] public void Blur() { - using var image = new Image(Configuration.Default, 400, 400, Color.White); + using Image image = new(Configuration.Default, 400, 400, Color.White); image.Mutate(c => c.BokehBlur()); } } diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs index 9727731b6e..ba37ee1661 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs @@ -7,6 +7,271 @@ namespace SixLabors.ImageSharp.Tests.Common; public partial class SimdUtilsTests { + public static readonly TheoryData MMShuffleData = new() + { + { SimdUtils.Shuffle.MMShuffle(0, 0, 0, 0), SimdUtils.Shuffle.MMShuffle0000 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 0, 1), SimdUtils.Shuffle.MMShuffle0001 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 0, 2), SimdUtils.Shuffle.MMShuffle0002 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 0, 3), SimdUtils.Shuffle.MMShuffle0003 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 1, 0), SimdUtils.Shuffle.MMShuffle0010 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 1, 1), SimdUtils.Shuffle.MMShuffle0011 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 1, 2), SimdUtils.Shuffle.MMShuffle0012 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 1, 3), SimdUtils.Shuffle.MMShuffle0013 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 2, 0), SimdUtils.Shuffle.MMShuffle0020 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 2, 1), SimdUtils.Shuffle.MMShuffle0021 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 2, 2), SimdUtils.Shuffle.MMShuffle0022 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 2, 3), SimdUtils.Shuffle.MMShuffle0023 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 3, 0), SimdUtils.Shuffle.MMShuffle0030 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 3, 1), SimdUtils.Shuffle.MMShuffle0031 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 3, 2), SimdUtils.Shuffle.MMShuffle0032 }, + { SimdUtils.Shuffle.MMShuffle(0, 0, 3, 3), SimdUtils.Shuffle.MMShuffle0033 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 0, 0), SimdUtils.Shuffle.MMShuffle0100 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 0, 1), SimdUtils.Shuffle.MMShuffle0101 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 0, 2), SimdUtils.Shuffle.MMShuffle0102 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 0, 3), SimdUtils.Shuffle.MMShuffle0103 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 1, 0), SimdUtils.Shuffle.MMShuffle0110 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 1, 1), SimdUtils.Shuffle.MMShuffle0111 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 1, 2), SimdUtils.Shuffle.MMShuffle0112 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 1, 3), SimdUtils.Shuffle.MMShuffle0113 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 2, 0), SimdUtils.Shuffle.MMShuffle0120 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 2, 1), SimdUtils.Shuffle.MMShuffle0121 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 2, 2), SimdUtils.Shuffle.MMShuffle0122 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 2, 3), SimdUtils.Shuffle.MMShuffle0123 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 3, 0), SimdUtils.Shuffle.MMShuffle0130 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 3, 1), SimdUtils.Shuffle.MMShuffle0131 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 3, 2), SimdUtils.Shuffle.MMShuffle0132 }, + { SimdUtils.Shuffle.MMShuffle(0, 1, 3, 3), SimdUtils.Shuffle.MMShuffle0133 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 0, 0), SimdUtils.Shuffle.MMShuffle0200 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 0, 1), SimdUtils.Shuffle.MMShuffle0201 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 0, 2), SimdUtils.Shuffle.MMShuffle0202 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 0, 3), SimdUtils.Shuffle.MMShuffle0203 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 1, 0), SimdUtils.Shuffle.MMShuffle0210 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 1, 1), SimdUtils.Shuffle.MMShuffle0211 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 1, 2), SimdUtils.Shuffle.MMShuffle0212 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 1, 3), SimdUtils.Shuffle.MMShuffle0213 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 2, 0), SimdUtils.Shuffle.MMShuffle0220 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 2, 1), SimdUtils.Shuffle.MMShuffle0221 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 2, 2), SimdUtils.Shuffle.MMShuffle0222 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 2, 3), SimdUtils.Shuffle.MMShuffle0223 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 3, 0), SimdUtils.Shuffle.MMShuffle0230 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 3, 1), SimdUtils.Shuffle.MMShuffle0231 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 3, 2), SimdUtils.Shuffle.MMShuffle0232 }, + { SimdUtils.Shuffle.MMShuffle(0, 2, 3, 3), SimdUtils.Shuffle.MMShuffle0233 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 0, 0), SimdUtils.Shuffle.MMShuffle0300 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 0, 1), SimdUtils.Shuffle.MMShuffle0301 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 0, 2), SimdUtils.Shuffle.MMShuffle0302 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 0, 3), SimdUtils.Shuffle.MMShuffle0303 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 1, 0), SimdUtils.Shuffle.MMShuffle0310 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 1, 1), SimdUtils.Shuffle.MMShuffle0311 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 1, 2), SimdUtils.Shuffle.MMShuffle0312 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 1, 3), SimdUtils.Shuffle.MMShuffle0313 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 2, 0), SimdUtils.Shuffle.MMShuffle0320 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 2, 1), SimdUtils.Shuffle.MMShuffle0321 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 2, 2), SimdUtils.Shuffle.MMShuffle0322 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 2, 3), SimdUtils.Shuffle.MMShuffle0323 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 3, 0), SimdUtils.Shuffle.MMShuffle0330 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 3, 1), SimdUtils.Shuffle.MMShuffle0331 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 3, 2), SimdUtils.Shuffle.MMShuffle0332 }, + { SimdUtils.Shuffle.MMShuffle(0, 3, 3, 3), SimdUtils.Shuffle.MMShuffle0333 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 0, 0), SimdUtils.Shuffle.MMShuffle1000 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 0, 1), SimdUtils.Shuffle.MMShuffle1001 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 0, 2), SimdUtils.Shuffle.MMShuffle1002 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 0, 3), SimdUtils.Shuffle.MMShuffle1003 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 1, 0), SimdUtils.Shuffle.MMShuffle1010 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 1, 1), SimdUtils.Shuffle.MMShuffle1011 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 1, 2), SimdUtils.Shuffle.MMShuffle1012 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 1, 3), SimdUtils.Shuffle.MMShuffle1013 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 2, 0), SimdUtils.Shuffle.MMShuffle1020 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 2, 1), SimdUtils.Shuffle.MMShuffle1021 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 2, 2), SimdUtils.Shuffle.MMShuffle1022 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 2, 3), SimdUtils.Shuffle.MMShuffle1023 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 3, 0), SimdUtils.Shuffle.MMShuffle1030 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 3, 1), SimdUtils.Shuffle.MMShuffle1031 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 3, 2), SimdUtils.Shuffle.MMShuffle1032 }, + { SimdUtils.Shuffle.MMShuffle(1, 0, 3, 3), SimdUtils.Shuffle.MMShuffle1033 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 0, 0), SimdUtils.Shuffle.MMShuffle1100 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 0, 1), SimdUtils.Shuffle.MMShuffle1101 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 0, 2), SimdUtils.Shuffle.MMShuffle1102 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 0, 3), SimdUtils.Shuffle.MMShuffle1103 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 1, 0), SimdUtils.Shuffle.MMShuffle1110 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 1, 1), SimdUtils.Shuffle.MMShuffle1111 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 1, 2), SimdUtils.Shuffle.MMShuffle1112 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 1, 3), SimdUtils.Shuffle.MMShuffle1113 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 2, 0), SimdUtils.Shuffle.MMShuffle1120 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 2, 1), SimdUtils.Shuffle.MMShuffle1121 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 2, 2), SimdUtils.Shuffle.MMShuffle1122 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 2, 3), SimdUtils.Shuffle.MMShuffle1123 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 3, 0), SimdUtils.Shuffle.MMShuffle1130 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 3, 1), SimdUtils.Shuffle.MMShuffle1131 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 3, 2), SimdUtils.Shuffle.MMShuffle1132 }, + { SimdUtils.Shuffle.MMShuffle(1, 1, 3, 3), SimdUtils.Shuffle.MMShuffle1133 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 0, 0), SimdUtils.Shuffle.MMShuffle1200 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 0, 1), SimdUtils.Shuffle.MMShuffle1201 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 0, 2), SimdUtils.Shuffle.MMShuffle1202 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 0, 3), SimdUtils.Shuffle.MMShuffle1203 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 1, 0), SimdUtils.Shuffle.MMShuffle1210 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 1, 1), SimdUtils.Shuffle.MMShuffle1211 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 1, 2), SimdUtils.Shuffle.MMShuffle1212 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 1, 3), SimdUtils.Shuffle.MMShuffle1213 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 2, 0), SimdUtils.Shuffle.MMShuffle1220 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 2, 1), SimdUtils.Shuffle.MMShuffle1221 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 2, 2), SimdUtils.Shuffle.MMShuffle1222 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 2, 3), SimdUtils.Shuffle.MMShuffle1223 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 3, 0), SimdUtils.Shuffle.MMShuffle1230 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 3, 1), SimdUtils.Shuffle.MMShuffle1231 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 3, 2), SimdUtils.Shuffle.MMShuffle1232 }, + { SimdUtils.Shuffle.MMShuffle(1, 2, 3, 3), SimdUtils.Shuffle.MMShuffle1233 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 0, 0), SimdUtils.Shuffle.MMShuffle1300 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 0, 1), SimdUtils.Shuffle.MMShuffle1301 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 0, 2), SimdUtils.Shuffle.MMShuffle1302 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 0, 3), SimdUtils.Shuffle.MMShuffle1303 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 1, 0), SimdUtils.Shuffle.MMShuffle1310 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 1, 1), SimdUtils.Shuffle.MMShuffle1311 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 1, 2), SimdUtils.Shuffle.MMShuffle1312 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 1, 3), SimdUtils.Shuffle.MMShuffle1313 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 2, 0), SimdUtils.Shuffle.MMShuffle1320 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 2, 1), SimdUtils.Shuffle.MMShuffle1321 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 2, 2), SimdUtils.Shuffle.MMShuffle1322 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 2, 3), SimdUtils.Shuffle.MMShuffle1323 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 3, 0), SimdUtils.Shuffle.MMShuffle1330 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 3, 1), SimdUtils.Shuffle.MMShuffle1331 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 3, 2), SimdUtils.Shuffle.MMShuffle1332 }, + { SimdUtils.Shuffle.MMShuffle(1, 3, 3, 3), SimdUtils.Shuffle.MMShuffle1333 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 0, 0), SimdUtils.Shuffle.MMShuffle2000 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 0, 1), SimdUtils.Shuffle.MMShuffle2001 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 0, 2), SimdUtils.Shuffle.MMShuffle2002 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 0, 3), SimdUtils.Shuffle.MMShuffle2003 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 1, 0), SimdUtils.Shuffle.MMShuffle2010 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 1, 1), SimdUtils.Shuffle.MMShuffle2011 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 1, 2), SimdUtils.Shuffle.MMShuffle2012 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 1, 3), SimdUtils.Shuffle.MMShuffle2013 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 2, 0), SimdUtils.Shuffle.MMShuffle2020 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 2, 1), SimdUtils.Shuffle.MMShuffle2021 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 2, 2), SimdUtils.Shuffle.MMShuffle2022 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 2, 3), SimdUtils.Shuffle.MMShuffle2023 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 3, 0), SimdUtils.Shuffle.MMShuffle2030 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 3, 1), SimdUtils.Shuffle.MMShuffle2031 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 3, 2), SimdUtils.Shuffle.MMShuffle2032 }, + { SimdUtils.Shuffle.MMShuffle(2, 0, 3, 3), SimdUtils.Shuffle.MMShuffle2033 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 0, 0), SimdUtils.Shuffle.MMShuffle2100 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 0, 1), SimdUtils.Shuffle.MMShuffle2101 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 0, 2), SimdUtils.Shuffle.MMShuffle2102 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 0, 3), SimdUtils.Shuffle.MMShuffle2103 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 1, 0), SimdUtils.Shuffle.MMShuffle2110 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 1, 1), SimdUtils.Shuffle.MMShuffle2111 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 1, 2), SimdUtils.Shuffle.MMShuffle2112 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 1, 3), SimdUtils.Shuffle.MMShuffle2113 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 2, 0), SimdUtils.Shuffle.MMShuffle2120 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 2, 1), SimdUtils.Shuffle.MMShuffle2121 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 2, 2), SimdUtils.Shuffle.MMShuffle2122 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 2, 3), SimdUtils.Shuffle.MMShuffle2123 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 3, 0), SimdUtils.Shuffle.MMShuffle2130 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 3, 1), SimdUtils.Shuffle.MMShuffle2131 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 3, 2), SimdUtils.Shuffle.MMShuffle2132 }, + { SimdUtils.Shuffle.MMShuffle(2, 1, 3, 3), SimdUtils.Shuffle.MMShuffle2133 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 0, 0), SimdUtils.Shuffle.MMShuffle2200 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 0, 1), SimdUtils.Shuffle.MMShuffle2201 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 0, 2), SimdUtils.Shuffle.MMShuffle2202 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 0, 3), SimdUtils.Shuffle.MMShuffle2203 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 1, 0), SimdUtils.Shuffle.MMShuffle2210 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 1, 1), SimdUtils.Shuffle.MMShuffle2211 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 1, 2), SimdUtils.Shuffle.MMShuffle2212 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 1, 3), SimdUtils.Shuffle.MMShuffle2213 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 2, 0), SimdUtils.Shuffle.MMShuffle2220 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 2, 1), SimdUtils.Shuffle.MMShuffle2221 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 2, 2), SimdUtils.Shuffle.MMShuffle2222 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 2, 3), SimdUtils.Shuffle.MMShuffle2223 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 3, 0), SimdUtils.Shuffle.MMShuffle2230 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 3, 1), SimdUtils.Shuffle.MMShuffle2231 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 3, 2), SimdUtils.Shuffle.MMShuffle2232 }, + { SimdUtils.Shuffle.MMShuffle(2, 2, 3, 3), SimdUtils.Shuffle.MMShuffle2233 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 0, 0), SimdUtils.Shuffle.MMShuffle2300 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 0, 1), SimdUtils.Shuffle.MMShuffle2301 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 0, 2), SimdUtils.Shuffle.MMShuffle2302 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 0, 3), SimdUtils.Shuffle.MMShuffle2303 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 1, 0), SimdUtils.Shuffle.MMShuffle2310 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 1, 1), SimdUtils.Shuffle.MMShuffle2311 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 1, 2), SimdUtils.Shuffle.MMShuffle2312 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 1, 3), SimdUtils.Shuffle.MMShuffle2313 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 2, 0), SimdUtils.Shuffle.MMShuffle2320 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 2, 1), SimdUtils.Shuffle.MMShuffle2321 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 2, 2), SimdUtils.Shuffle.MMShuffle2322 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 2, 3), SimdUtils.Shuffle.MMShuffle2323 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 3, 0), SimdUtils.Shuffle.MMShuffle2330 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 3, 1), SimdUtils.Shuffle.MMShuffle2331 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 3, 2), SimdUtils.Shuffle.MMShuffle2332 }, + { SimdUtils.Shuffle.MMShuffle(2, 3, 3, 3), SimdUtils.Shuffle.MMShuffle2333 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 0, 0), SimdUtils.Shuffle.MMShuffle3000 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 0, 1), SimdUtils.Shuffle.MMShuffle3001 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 0, 2), SimdUtils.Shuffle.MMShuffle3002 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 0, 3), SimdUtils.Shuffle.MMShuffle3003 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 1, 0), SimdUtils.Shuffle.MMShuffle3010 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 1, 1), SimdUtils.Shuffle.MMShuffle3011 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 1, 2), SimdUtils.Shuffle.MMShuffle3012 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 1, 3), SimdUtils.Shuffle.MMShuffle3013 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 2, 0), SimdUtils.Shuffle.MMShuffle3020 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 2, 1), SimdUtils.Shuffle.MMShuffle3021 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 2, 2), SimdUtils.Shuffle.MMShuffle3022 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 2, 3), SimdUtils.Shuffle.MMShuffle3023 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 3, 0), SimdUtils.Shuffle.MMShuffle3030 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 3, 1), SimdUtils.Shuffle.MMShuffle3031 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 3, 2), SimdUtils.Shuffle.MMShuffle3032 }, + { SimdUtils.Shuffle.MMShuffle(3, 0, 3, 3), SimdUtils.Shuffle.MMShuffle3033 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 0, 0), SimdUtils.Shuffle.MMShuffle3100 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 0, 1), SimdUtils.Shuffle.MMShuffle3101 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 0, 2), SimdUtils.Shuffle.MMShuffle3102 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 0, 3), SimdUtils.Shuffle.MMShuffle3103 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 1, 0), SimdUtils.Shuffle.MMShuffle3110 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 1, 1), SimdUtils.Shuffle.MMShuffle3111 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 1, 2), SimdUtils.Shuffle.MMShuffle3112 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 1, 3), SimdUtils.Shuffle.MMShuffle3113 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 2, 0), SimdUtils.Shuffle.MMShuffle3120 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 2, 1), SimdUtils.Shuffle.MMShuffle3121 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 2, 2), SimdUtils.Shuffle.MMShuffle3122 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 2, 3), SimdUtils.Shuffle.MMShuffle3123 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 3, 0), SimdUtils.Shuffle.MMShuffle3130 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 3, 1), SimdUtils.Shuffle.MMShuffle3131 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 3, 2), SimdUtils.Shuffle.MMShuffle3132 }, + { SimdUtils.Shuffle.MMShuffle(3, 1, 3, 3), SimdUtils.Shuffle.MMShuffle3133 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 0, 0), SimdUtils.Shuffle.MMShuffle3200 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 0, 1), SimdUtils.Shuffle.MMShuffle3201 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 0, 2), SimdUtils.Shuffle.MMShuffle3202 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 0, 3), SimdUtils.Shuffle.MMShuffle3203 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 1, 0), SimdUtils.Shuffle.MMShuffle3210 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 1, 1), SimdUtils.Shuffle.MMShuffle3211 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 1, 2), SimdUtils.Shuffle.MMShuffle3212 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 1, 3), SimdUtils.Shuffle.MMShuffle3213 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 2, 0), SimdUtils.Shuffle.MMShuffle3220 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 2, 1), SimdUtils.Shuffle.MMShuffle3221 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 2, 2), SimdUtils.Shuffle.MMShuffle3222 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 2, 3), SimdUtils.Shuffle.MMShuffle3223 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 3, 0), SimdUtils.Shuffle.MMShuffle3230 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 3, 1), SimdUtils.Shuffle.MMShuffle3231 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 3, 2), SimdUtils.Shuffle.MMShuffle3232 }, + { SimdUtils.Shuffle.MMShuffle(3, 2, 3, 3), SimdUtils.Shuffle.MMShuffle3233 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 0, 0), SimdUtils.Shuffle.MMShuffle3300 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 0, 1), SimdUtils.Shuffle.MMShuffle3301 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 0, 2), SimdUtils.Shuffle.MMShuffle3302 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 0, 3), SimdUtils.Shuffle.MMShuffle3303 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 1, 0), SimdUtils.Shuffle.MMShuffle3310 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 1, 1), SimdUtils.Shuffle.MMShuffle3311 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 1, 2), SimdUtils.Shuffle.MMShuffle3312 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 1, 3), SimdUtils.Shuffle.MMShuffle3313 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 2, 0), SimdUtils.Shuffle.MMShuffle3320 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 2, 1), SimdUtils.Shuffle.MMShuffle3321 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 2, 2), SimdUtils.Shuffle.MMShuffle3322 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 2, 3), SimdUtils.Shuffle.MMShuffle3323 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 3, 0), SimdUtils.Shuffle.MMShuffle3330 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 3, 1), SimdUtils.Shuffle.MMShuffle3331 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 3, 2), SimdUtils.Shuffle.MMShuffle3332 }, + { SimdUtils.Shuffle.MMShuffle(3, 3, 3, 3), SimdUtils.Shuffle.MMShuffle3333 } + }; + + [Theory] + [MemberData(nameof(MMShuffleData))] + public void MMShuffleConstantsAreCorrect(byte expected, byte actual) + => Assert.Equal(expected, actual); + [Theory] [MemberData(nameof(ArraySizesDivisibleBy4))] public void BulkShuffleFloat4Channel(int count) @@ -16,7 +281,7 @@ public partial class SimdUtilsTests // No need to test multiple shuffle controls as the // pipeline is always the same. int size = FeatureTestRunner.Deserialize(serialized); - byte control = default(WZYXShuffle4).Control; + const byte control = SimdUtils.Shuffle.MMShuffle0123; TestShuffleFloat4Channel( size, @@ -45,39 +310,39 @@ public partial class SimdUtilsTests TestShuffleByte4Channel( size, (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, wxyz), - wxyz.Control); + SimdUtils.Shuffle.MMShuffle2103); WZYXShuffle4 wzyx = default; TestShuffleByte4Channel( size, (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, wzyx), - wzyx.Control); + SimdUtils.Shuffle.MMShuffle0123); YZWXShuffle4 yzwx = default; TestShuffleByte4Channel( size, (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, yzwx), - yzwx.Control); + SimdUtils.Shuffle.MMShuffle0321); ZYXWShuffle4 zyxw = default; TestShuffleByte4Channel( size, (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, zyxw), - zyxw.Control); + SimdUtils.Shuffle.MMShuffle3012); - var xwyz = new DefaultShuffle4(2, 1, 3, 0); + DefaultShuffle4 xwyz = new(SimdUtils.Shuffle.MMShuffle2130); TestShuffleByte4Channel( size, (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, xwyz), xwyz.Control); - var yyyy = new DefaultShuffle4(1, 1, 1, 1); + DefaultShuffle4 yyyy = new(SimdUtils.Shuffle.MMShuffle1111); TestShuffleByte4Channel( size, (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, yyyy), yyyy.Control); - var wwww = new DefaultShuffle4(3, 3, 3, 3); + DefaultShuffle4 wwww = new(SimdUtils.Shuffle.MMShuffle3333); TestShuffleByte4Channel( size, (s, d) => SimdUtils.Shuffle4(s.Span, d.Span, wwww), @@ -101,25 +366,25 @@ public partial class SimdUtilsTests // These cannot be expressed as a theory as you cannot // use RemoteExecutor within generic methods nor pass // IShuffle3 to the generic utils method. - var zyx = new DefaultShuffle3(0, 1, 2); + DefaultShuffle3 zyx = new(SimdUtils.Shuffle.MMShuffle3012); TestShuffleByte3Channel( size, (s, d) => SimdUtils.Shuffle3(s.Span, d.Span, zyx), zyx.Control); - var xyz = new DefaultShuffle3(2, 1, 0); + DefaultShuffle3 xyz = new(SimdUtils.Shuffle.MMShuffle3210); TestShuffleByte3Channel( size, (s, d) => SimdUtils.Shuffle3(s.Span, d.Span, xyz), xyz.Control); - var yyy = new DefaultShuffle3(1, 1, 1); + DefaultShuffle3 yyy = new(SimdUtils.Shuffle.MMShuffle3111); TestShuffleByte3Channel( size, (s, d) => SimdUtils.Shuffle3(s.Span, d.Span, yyy), yyy.Control); - var zzz = new DefaultShuffle3(2, 2, 2); + DefaultShuffle3 zzz = new(SimdUtils.Shuffle.MMShuffle3222); TestShuffleByte3Channel( size, (s, d) => SimdUtils.Shuffle3(s.Span, d.Span, zzz), @@ -147,21 +412,21 @@ public partial class SimdUtilsTests TestPad3Shuffle4Channel( size, (s, d) => SimdUtils.Pad3Shuffle4(s.Span, d.Span, xyzw), - xyzw.Control); + SimdUtils.Shuffle.MMShuffle3210); - var xwyz = new DefaultPad3Shuffle4(2, 1, 3, 0); + DefaultPad3Shuffle4 xwyz = new(SimdUtils.Shuffle.MMShuffle2130); TestPad3Shuffle4Channel( size, (s, d) => SimdUtils.Pad3Shuffle4(s.Span, d.Span, xwyz), xwyz.Control); - var yyyy = new DefaultPad3Shuffle4(1, 1, 1, 1); + DefaultPad3Shuffle4 yyyy = new(SimdUtils.Shuffle.MMShuffle1111); TestPad3Shuffle4Channel( size, (s, d) => SimdUtils.Pad3Shuffle4(s.Span, d.Span, yyyy), yyyy.Control); - var wwww = new DefaultPad3Shuffle4(3, 3, 3, 3); + DefaultPad3Shuffle4 wwww = new(SimdUtils.Shuffle.MMShuffle3333); TestPad3Shuffle4Channel( size, (s, d) => SimdUtils.Pad3Shuffle4(s.Span, d.Span, wwww), @@ -189,21 +454,21 @@ public partial class SimdUtilsTests TestShuffle4Slice3Channel( size, (s, d) => SimdUtils.Shuffle4Slice3(s.Span, d.Span, xyzw), - xyzw.Control); + SimdUtils.Shuffle.MMShuffle3210); - var xwyz = new DefaultShuffle4Slice3(2, 1, 3, 0); + DefaultShuffle4Slice3 xwyz = new(SimdUtils.Shuffle.MMShuffle2130); TestShuffle4Slice3Channel( size, (s, d) => SimdUtils.Shuffle4Slice3(s.Span, d.Span, xwyz), xwyz.Control); - var yyyy = new DefaultShuffle4Slice3(1, 1, 1, 1); + DefaultShuffle4Slice3 yyyy = new(SimdUtils.Shuffle.MMShuffle1111); TestShuffle4Slice3Channel( size, (s, d) => SimdUtils.Shuffle4Slice3(s.Span, d.Span, yyyy), yyyy.Control); - var wwww = new DefaultShuffle4Slice3(3, 3, 3, 3); + DefaultShuffle4Slice3 wwww = new(SimdUtils.Shuffle.MMShuffle3333); TestShuffle4Slice3Channel( size, (s, d) => SimdUtils.Shuffle4Slice3(s.Span, d.Span, wwww), @@ -222,16 +487,16 @@ public partial class SimdUtilsTests byte control) { float[] source = new Random(count).GenerateRandomFloatArray(count, 0, 256); - var result = new float[count]; + float[] result = new float[count]; float[] expected = new float[count]; - SimdUtils.Shuffle.InverseMmShuffle( + SimdUtils.Shuffle.InverseMMShuffle( control, - out int p3, - out int p2, - out int p1, - out int p0); + out uint p3, + out uint p2, + out uint p1, + out uint p0); for (int i = 0; i < expected.Length; i += 4) { @@ -253,16 +518,16 @@ public partial class SimdUtilsTests { byte[] source = new byte[count]; new Random(count).NextBytes(source); - var result = new byte[count]; + byte[] result = new byte[count]; byte[] expected = new byte[count]; - SimdUtils.Shuffle.InverseMmShuffle( + SimdUtils.Shuffle.InverseMMShuffle( control, - out int p3, - out int p2, - out int p1, - out int p0); + out uint p3, + out uint p2, + out uint p1, + out uint p0); for (int i = 0; i < expected.Length; i += 4) { @@ -284,16 +549,16 @@ public partial class SimdUtilsTests { byte[] source = new byte[count]; new Random(count).NextBytes(source); - var result = new byte[count]; + byte[] result = new byte[count]; byte[] expected = new byte[count]; - SimdUtils.Shuffle.InverseMmShuffle( + SimdUtils.Shuffle.InverseMMShuffle( control, - out int _, - out int p2, - out int p1, - out int p0); + out uint _, + out uint p2, + out uint p1, + out uint p0); for (int i = 0; i < expected.Length; i += 3) { @@ -315,16 +580,16 @@ public partial class SimdUtilsTests byte[] source = new byte[count]; new Random(count).NextBytes(source); - var result = new byte[count * 4 / 3]; + byte[] result = new byte[count * 4 / 3]; byte[] expected = new byte[result.Length]; - SimdUtils.Shuffle.InverseMmShuffle( + SimdUtils.Shuffle.InverseMMShuffle( control, - out int p3, - out int p2, - out int p1, - out int p0); + out uint p3, + out uint p2, + out uint p1, + out uint p0); for (int i = 0, j = 0; i < expected.Length; i += 4, j += 3) { @@ -342,10 +607,10 @@ public partial class SimdUtilsTests temp[2] = source[j + 2]; temp[3] = byte.MaxValue; - expected[i] = temp[p0]; - expected[i + 1] = temp[p1]; - expected[i + 2] = temp[p2]; - expected[i + 3] = temp[p3]; + expected[i] = temp[(int)p0]; + expected[i + 1] = temp[(int)p1]; + expected[i + 2] = temp[(int)p2]; + expected[i + 3] = temp[(int)p3]; } convert(source, result); @@ -366,16 +631,16 @@ public partial class SimdUtilsTests byte[] source = new byte[count]; new Random(count).NextBytes(source); - var result = new byte[count * 3 / 4]; + byte[] result = new byte[count * 3 / 4]; byte[] expected = new byte[result.Length]; - SimdUtils.Shuffle.InverseMmShuffle( + SimdUtils.Shuffle.InverseMMShuffle( control, - out int _, - out int p2, - out int p1, - out int p0); + out uint _, + out uint p2, + out uint p1, + out uint p0); for (int i = 0, j = 0; i < expected.Length; i += 3, j += 4) { diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs index 7def2003ae..40ac94eea6 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using Microsoft.CodeAnalysis; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Metadata; @@ -30,7 +31,7 @@ public class GifMetadataTests [Fact] public void CloneIsDeep() { - var meta = new GifMetadata + GifMetadata meta = new() { RepeatCount = 1, ColorTableMode = GifColorTableMode.Global, @@ -38,7 +39,7 @@ public class GifMetadataTests Comments = new List { "Foo" } }; - var clone = (GifMetadata)meta.DeepClone(); + GifMetadata clone = (GifMetadata)meta.DeepClone(); clone.RepeatCount = 2; clone.ColorTableMode = GifColorTableMode.Local; @@ -54,7 +55,7 @@ public class GifMetadataTests [Fact] public void Decode_IgnoreMetadataIsFalse_CommentsAreRead() { - var testFile = TestFile.Create(TestImages.Gif.Rings); + TestFile testFile = TestFile.Create(TestImages.Gif.Rings); using Image image = testFile.CreateRgba32Image(GifDecoder.Instance); GifMetadata metadata = image.Metadata.GetGifMetadata(); @@ -70,7 +71,7 @@ public class GifMetadataTests SkipMetadata = true }; - var testFile = TestFile.Create(TestImages.Gif.Rings); + TestFile testFile = TestFile.Create(TestImages.Gif.Rings); using Image image = testFile.CreateRgba32Image(GifDecoder.Instance, options); GifMetadata metadata = image.Metadata.GetGifMetadata(); @@ -80,7 +81,7 @@ public class GifMetadataTests [Fact] public void Decode_CanDecodeLargeTextComment() { - var testFile = TestFile.Create(TestImages.Gif.LargeComment); + TestFile testFile = TestFile.Create(TestImages.Gif.LargeComment); using Image image = testFile.CreateRgba32Image(GifDecoder.Instance); GifMetadata metadata = image.Metadata.GetGifMetadata(); @@ -92,11 +93,11 @@ public class GifMetadataTests [Fact] public void Encode_PreservesTextData() { - var decoder = GifDecoder.Instance; - var testFile = TestFile.Create(TestImages.Gif.LargeComment); + GifDecoder decoder = GifDecoder.Instance; + TestFile testFile = TestFile.Create(TestImages.Gif.LargeComment); using Image input = testFile.CreateRgba32Image(decoder); - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); input.Save(memoryStream, new GifEncoder()); memoryStream.Position = 0; @@ -111,8 +112,8 @@ public class GifMetadataTests [MemberData(nameof(RatioFiles))] public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); ImageInfo image = GifDecoder.Instance.Identify(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); @@ -124,8 +125,8 @@ public class GifMetadataTests [MemberData(nameof(RatioFiles))] public async Task Identify_VerifyRatioAsync(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); ImageInfo image = await GifDecoder.Instance.IdentifyAsync(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); @@ -137,8 +138,8 @@ public class GifMetadataTests [MemberData(nameof(RatioFiles))] public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); using Image image = GifDecoder.Instance.Decode(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); @@ -150,8 +151,8 @@ public class GifMetadataTests [MemberData(nameof(RatioFiles))] public async Task Decode_VerifyRatioAsync(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); using Image image = await GifDecoder.Instance.DecodeAsync(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); @@ -163,8 +164,8 @@ public class GifMetadataTests [MemberData(nameof(RepeatFiles))] public void Identify_VerifyRepeatCount(string imagePath, uint repeatCount) { - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); ImageInfo image = GifDecoder.Instance.Identify(DecoderOptions.Default, stream); GifMetadata meta = image.Metadata.GetGifMetadata(); Assert.Equal(repeatCount, meta.RepeatCount); @@ -174,10 +175,38 @@ public class GifMetadataTests [MemberData(nameof(RepeatFiles))] public void Decode_VerifyRepeatCount(string imagePath, uint repeatCount) { - var testFile = TestFile.Create(imagePath); - using var stream = new MemoryStream(testFile.Bytes, false); + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); using Image image = GifDecoder.Instance.Decode(DecoderOptions.Default, stream); GifMetadata meta = image.Metadata.GetGifMetadata(); Assert.Equal(repeatCount, meta.RepeatCount); } + + [Theory] + [InlineData(TestImages.Gif.Cheers, 93, GifColorTableMode.Global, 256, 4, GifDisposalMethod.NotDispose)] + public void Identify_Frames( + string imagePath, + int framesCount, + GifColorTableMode colorTableMode, + int globalColorTableLength, + int frameDelay, + GifDisposalMethod disposalMethod) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + + ImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + GifMetadata gifMetadata = imageInfo.Metadata.GetGifMetadata(); + Assert.NotNull(gifMetadata); + + Assert.Equal(framesCount, imageInfo.FrameMetadataCollection.Count); + GifFrameMetadata gifFrameMetadata = imageInfo.FrameMetadataCollection[imageInfo.FrameMetadataCollection.Count - 1].GetGifMetadata(); + + Assert.Equal(colorTableMode, gifFrameMetadata.ColorTableMode); + Assert.Equal(globalColorTableLength, gifFrameMetadata.ColorTableLength); + Assert.Equal(frameDelay, gifFrameMetadata.FrameDelay); + Assert.Equal(disposalMethod, gifFrameMetadata.DisposalMethod); + } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 3e4bcae6b7..cde9e776b2 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -99,8 +99,7 @@ public partial class Block8x8FTests : JpegFixture Times, () => { - var b = default(Block8x8F); - b.LoadFrom(data); + Block8x8F b = Block8x8F.Load(data); b.ScaledCopyTo(mirror); }); @@ -117,8 +116,7 @@ public partial class Block8x8FTests : JpegFixture float[] expected = Create8x8FloatData(); ReferenceImplementations.Transpose8x8(expected); - var block8x8 = default(Block8x8F); - block8x8.LoadFrom(Create8x8FloatData()); + Block8x8F block8x8 = Block8x8F.Load(Create8x8FloatData()); block8x8.TransposeInplace(); @@ -153,9 +151,8 @@ public partial class Block8x8FTests : JpegFixture [Fact] public void NormalizeColors() { - var block = default(Block8x8F); float[] input = Create8x8ColorCropTestData(); - block.LoadFrom(input); + Block8x8F block = Block8x8F.Load(input); this.Output.WriteLine("Input:"); this.PrintLinearData(input); @@ -242,8 +239,7 @@ public partial class Block8x8FTests : JpegFixture { float[] data = Create8x8RandomFloatData(-1000, 1000); - var source = default(Block8x8F); - source.LoadFrom(data); + Block8x8F source = Block8x8F.Load(data); var dest = default(Block8x8); source.RoundInto(ref dest); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs index 798ea30407..b5d364dd38 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs @@ -269,8 +269,7 @@ public class Block8x8Tests : JpegFixture short[] expected = Create8x8ShortData(); ReferenceImplementations.Transpose8x8(expected); - var block8x8 = default(Block8x8); - block8x8.LoadFrom(Create8x8ShortData()); + Block8x8 block8x8 = Block8x8.Load(Create8x8ShortData()); block8x8.TransposeInplace(); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs index 5853ff37a5..5a1488c411 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs @@ -114,8 +114,7 @@ public static class DCTTests int seed = FeatureTestRunner.Deserialize(serialized); Span src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed); - var srcBlock = default(Block8x8F); - srcBlock.LoadFrom(src); + Block8x8F srcBlock = Block8x8F.Load(src); float[] expectedDest = new float[64]; float[] temp = new float[64]; @@ -162,8 +161,7 @@ public static class DCTTests public void TranformIDCT_4x4(int seed) { Span src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed, 4, 4); - var srcBlock = default(Block8x8F); - srcBlock.LoadFrom(src); + Block8x8F srcBlock = Block8x8F.Load(src); float[] expectedDest = new float[64]; float[] temp = new float[64]; @@ -224,8 +222,7 @@ public static class DCTTests public void TranformIDCT_2x2(int seed) { Span src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed, 2, 2); - var srcBlock = default(Block8x8F); - srcBlock.LoadFrom(src); + Block8x8F srcBlock = Block8x8F.Load(src); float[] expectedDest = new float[64]; float[] temp = new float[64]; @@ -286,8 +283,7 @@ public static class DCTTests public void TranformIDCT_1x1(int seed) { Span src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed, 1, 1); - var srcBlock = default(Block8x8F); - srcBlock.LoadFrom(src); + Block8x8F srcBlock = Block8x8F.Load(src); float[] expectedDest = new float[64]; float[] temp = new float[64]; @@ -330,8 +326,7 @@ public static class DCTTests int seed = FeatureTestRunner.Deserialize(serialized); Span src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed); - var block = default(Block8x8F); - block.LoadFrom(src); + Block8x8F block = Block8x8F.Load(src); float[] expectedDest = new float[64]; float[] temp1 = new float[64]; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index a939f1b687..ef9f48a890 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.X86; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.Formats.Jpeg.Components; @@ -20,7 +22,7 @@ public class JpegColorConverterTests private const int TestBufferLength = 40; - private const HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX; + private const HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2; private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new(epsilon: Precision); @@ -36,7 +38,7 @@ public class JpegColorConverterTests [Fact] public void GetConverterThrowsExceptionOnInvalidColorSpace() { - var invalidColorSpace = (JpegColorSpace)(-1); + JpegColorSpace invalidColorSpace = (JpegColorSpace)(-1); Assert.Throws(() => JpegColorConverterBase.GetConverter(invalidColorSpace, 8)); } @@ -61,7 +63,7 @@ public class JpegColorConverterTests [InlineData(JpegColorSpace.YCbCr, 12)] internal void GetConverterReturnsValidConverter(JpegColorSpace colorSpace, int precision) { - var converter = JpegColorConverterBase.GetConverter(colorSpace, precision); + JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(colorSpace, precision); Assert.NotNull(converter); Assert.True(converter.IsAvailable); @@ -69,16 +71,181 @@ public class JpegColorConverterTests Assert.Equal(precision, converter.Precision); } + [Fact] + public void GetConverterReturnsCorrectConverterWithRgbColorSpace() + { + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunTest, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + + static void RunTest(string arg) + { + // arrange + Type expectedType = typeof(JpegColorConverterBase.RgbScalar); + if (Avx.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.RgbAvx); + } + else if (Sse2.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.RgbVector); + } + else if (AdvSimd.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.RgbArm); + } + + // act + JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.RGB, 8); + Type actualType = converter.GetType(); + + // assert + Assert.Equal(expectedType, actualType); + } + } + + [Fact] + public void GetConverterReturnsCorrectConverterWithGrayScaleColorSpace() + { + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunTest, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + + static void RunTest(string arg) + { + // arrange + Type expectedType = typeof(JpegColorConverterBase.GrayscaleScalar); + if (Avx.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.GrayscaleAvx); + } + else if (Sse2.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.GrayScaleVector); + } + else if (AdvSimd.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.GrayscaleArm); + } + + // act + JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.Grayscale, 8); + Type actualType = converter.GetType(); + + // assert + Assert.Equal(expectedType, actualType); + } + } + + [Fact] + public void GetConverterReturnsCorrectConverterWithCmykColorSpace() + { + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunTest, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + + static void RunTest(string arg) + { + // arrange + Type expectedType = typeof(JpegColorConverterBase.CmykScalar); + if (Avx.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.CmykAvx); + } + else if (Sse2.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.CmykVector); + } + else if (AdvSimd.Arm64.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.CmykArm64); + } + + // act + JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.Cmyk, 8); + Type actualType = converter.GetType(); + + // assert + Assert.Equal(expectedType, actualType); + } + } + + [Fact] + public void GetConverterReturnsCorrectConverterWithYCbCrColorSpace() + { + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunTest, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + + static void RunTest(string arg) + { + // arrange + Type expectedType = typeof(JpegColorConverterBase.YCbCrScalar); + if (Avx.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.YCbCrAvx); + } + else if (Sse2.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.YCbCrVector); + } + else if (AdvSimd.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.YCbCrArm); + } + + // act + JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.YCbCr, 8); + Type actualType = converter.GetType(); + + // assert + Assert.Equal(expectedType, actualType); + } + } + + [Fact] + public void GetConverterReturnsCorrectConverterWithYcckColorSpace() + { + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunTest, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + + static void RunTest(string arg) + { + // arrange + Type expectedType = typeof(JpegColorConverterBase.YccKScalar); + if (Avx.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.YccKAvx); + } + else if (Sse2.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.YccKVector); + } + else if (AdvSimd.Arm64.IsSupported) + { + expectedType = typeof(JpegColorConverterBase.YccKArm64); + } + + // act + JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(JpegColorSpace.Ycck, 8); + Type actualType = converter.GetType(); + + // assert + Assert.Equal(expectedType, actualType); + } + } + [Theory] [InlineData(JpegColorSpace.Grayscale, 1)] [InlineData(JpegColorSpace.Ycck, 4)] [InlineData(JpegColorSpace.Cmyk, 4)] [InlineData(JpegColorSpace.RGB, 3)] [InlineData(JpegColorSpace.YCbCr, 3)] - internal void ConvertWithSelectedConverter(JpegColorSpace colorSpace, int componentCount) + internal void ConvertToRgbWithSelectedConverter(JpegColorSpace colorSpace, int componentCount) { - var converter = JpegColorConverterBase.GetConverter(colorSpace, 8); - ValidateConversion( + JpegColorConverterBase converter = JpegColorConverterBase.GetConverter(colorSpace, 8); + ValidateConversionToRgb( converter, componentCount, 1); @@ -87,13 +254,13 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromYCbCrBasic(int seed) => - this.TestConverter(new JpegColorConverterBase.YCbCrScalar(8), 3, seed); + this.TestConversionToRgb(new JpegColorConverterBase.YCbCrScalar(8), 3, seed); [Theory] [MemberData(nameof(Seeds))] public void FromYCbCrVector(int seed) { - var converter = new JpegColorConverterBase.YCbCrVector(8); + JpegColorConverterBase.YCbCrVector converter = new(8); if (!converter.IsAvailable) { @@ -108,22 +275,23 @@ public class JpegColorConverterTests IntrinsicsConfig); static void RunTest(string arg) => - ValidateConversion( + ValidateConversionToRgb( new JpegColorConverterBase.YCbCrVector(8), 3, - FeatureTestRunner.Deserialize(arg)); + FeatureTestRunner.Deserialize(arg), + new JpegColorConverterBase.YCbCrScalar(8)); } [Theory] [MemberData(nameof(Seeds))] public void FromCmykBasic(int seed) => - this.TestConverter(new JpegColorConverterBase.CmykScalar(8), 4, seed); + this.TestConversionToRgb(new JpegColorConverterBase.CmykScalar(8), 4, seed); [Theory] [MemberData(nameof(Seeds))] public void FromCmykVector(int seed) { - var converter = new JpegColorConverterBase.CmykVector(8); + JpegColorConverterBase.CmykVector converter = new(8); if (!converter.IsAvailable) { @@ -138,22 +306,23 @@ public class JpegColorConverterTests IntrinsicsConfig); static void RunTest(string arg) => - ValidateConversion( + ValidateConversionToRgb( new JpegColorConverterBase.CmykVector(8), 4, - FeatureTestRunner.Deserialize(arg)); + FeatureTestRunner.Deserialize(arg), + new JpegColorConverterBase.CmykScalar(8)); } [Theory] [MemberData(nameof(Seeds))] public void FromGrayscaleBasic(int seed) => - this.TestConverter(new JpegColorConverterBase.GrayscaleScalar(8), 1, seed); + this.TestConversionToRgb(new JpegColorConverterBase.GrayscaleScalar(8), 1, seed); [Theory] [MemberData(nameof(Seeds))] public void FromGrayscaleVector(int seed) { - var converter = new JpegColorConverterBase.GrayScaleVector(8); + JpegColorConverterBase.GrayScaleVector converter = new(8); if (!converter.IsAvailable) { @@ -168,22 +337,23 @@ public class JpegColorConverterTests IntrinsicsConfig); static void RunTest(string arg) => - ValidateConversion( + ValidateConversionToRgb( new JpegColorConverterBase.GrayScaleVector(8), 1, - FeatureTestRunner.Deserialize(arg)); + FeatureTestRunner.Deserialize(arg), + new JpegColorConverterBase.GrayscaleScalar(8)); } [Theory] [MemberData(nameof(Seeds))] public void FromRgbBasic(int seed) => - this.TestConverter(new JpegColorConverterBase.RgbScalar(8), 3, seed); + this.TestConversionToRgb(new JpegColorConverterBase.RgbScalar(8), 3, seed); [Theory] [MemberData(nameof(Seeds))] public void FromRgbVector(int seed) { - var converter = new JpegColorConverterBase.RgbVector(8); + JpegColorConverterBase.RgbVector converter = new(8); if (!converter.IsAvailable) { @@ -198,22 +368,23 @@ public class JpegColorConverterTests IntrinsicsConfig); static void RunTest(string arg) => - ValidateConversion( + ValidateConversionToRgb( new JpegColorConverterBase.RgbVector(8), 3, - FeatureTestRunner.Deserialize(arg)); + FeatureTestRunner.Deserialize(arg), + new JpegColorConverterBase.RgbScalar(8)); } [Theory] [MemberData(nameof(Seeds))] public void FromYccKBasic(int seed) => - this.TestConverter(new JpegColorConverterBase.YccKScalar(8), 4, seed); + this.TestConversionToRgb(new JpegColorConverterBase.YccKScalar(8), 4, seed); [Theory] [MemberData(nameof(Seeds))] public void FromYccKVector(int seed) { - var converter = new JpegColorConverterBase.YccKVector(8); + JpegColorConverterBase.YccKVector converter = new(8); if (!converter.IsAvailable) { @@ -228,41 +399,203 @@ public class JpegColorConverterTests IntrinsicsConfig); static void RunTest(string arg) => - ValidateConversion( + ValidateConversionToRgb( new JpegColorConverterBase.YccKVector(8), 4, - FeatureTestRunner.Deserialize(arg)); + FeatureTestRunner.Deserialize(arg), + new JpegColorConverterBase.YccKScalar(8)); } [Theory] [MemberData(nameof(Seeds))] public void FromYCbCrAvx2(int seed) => - this.TestConverter(new JpegColorConverterBase.YCbCrAvx(8), 3, seed); + this.TestConversionToRgb( + new JpegColorConverterBase.YCbCrAvx(8), + 3, + seed, + new JpegColorConverterBase.YCbCrScalar(8)); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToYCbCrAvx2(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.YCbCrAvx(8), + 3, + seed, + new JpegColorConverterBase.YCbCrScalar(8), + precísion: 2); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromYCbCrArm(int seed) => + this.TestConversionToRgb(new JpegColorConverterBase.YCbCrArm(8), + 3, + seed, + new JpegColorConverterBase.YCbCrScalar(8)); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToYCbCrArm(int seed) => + this.TestConversionFromRgb(new JpegColorConverterBase.YCbCrArm(8), + 3, + seed, + new JpegColorConverterBase.YCbCrScalar(8), + precísion: 2); [Theory] [MemberData(nameof(Seeds))] public void FromCmykAvx2(int seed) => - this.TestConverter(new JpegColorConverterBase.CmykAvx(8), 4, seed); + this.TestConversionToRgb( + new JpegColorConverterBase.CmykAvx(8), + 4, + seed, + new JpegColorConverterBase.CmykScalar(8)); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToCmykAvx2(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.CmykAvx(8), + 4, + seed, + new JpegColorConverterBase.CmykScalar(8), + precísion: 4); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromCmykArm(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.CmykArm64(8), + 4, + seed, + new JpegColorConverterBase.CmykScalar(8)); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToCmykArm(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.CmykArm64(8), + 4, + seed, + new JpegColorConverterBase.CmykScalar(8), + precísion: 4); [Theory] [MemberData(nameof(Seeds))] public void FromGrayscaleAvx2(int seed) => - this.TestConverter(new JpegColorConverterBase.GrayscaleAvx(8), 1, seed); + this.TestConversionToRgb( + new JpegColorConverterBase.GrayscaleAvx(8), + 1, + seed, + new JpegColorConverterBase.GrayscaleScalar(8)); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToGrayscaleAvx2(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.GrayscaleAvx(8), + 1, + seed, + new JpegColorConverterBase.GrayscaleScalar(8), + precísion: 3); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromGrayscaleArm(int seed) => + this.TestConversionToRgb(new JpegColorConverterBase.GrayscaleArm(8), + 1, + seed, + new JpegColorConverterBase.GrayscaleScalar(8)); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToGrayscaleArm(int seed) => + this.TestConversionFromRgb(new JpegColorConverterBase.GrayscaleArm(8), + 1, + seed, + new JpegColorConverterBase.GrayscaleScalar(8), + precísion: 3); [Theory] [MemberData(nameof(Seeds))] public void FromRgbAvx2(int seed) => - this.TestConverter(new JpegColorConverterBase.RgbAvx(8), 3, seed); + this.TestConversionToRgb( + new JpegColorConverterBase.RgbAvx(8), + 3, + seed, + new JpegColorConverterBase.RgbScalar(8)); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbArm(int seed) => + this.TestConversionToRgb( + new JpegColorConverterBase.RgbArm(8), + 3, + seed, + new JpegColorConverterBase.RgbScalar(8)); [Theory] [MemberData(nameof(Seeds))] public void FromYccKAvx2(int seed) => - this.TestConverter(new JpegColorConverterBase.YccKAvx(8), 4, seed); + this.TestConversionToRgb( + new JpegColorConverterBase.YccKAvx(8), + 4, + seed, + new JpegColorConverterBase.YccKScalar(8)); - private void TestConverter( + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToYccKAvx2(int seed) => + this.TestConversionFromRgb( + new JpegColorConverterBase.YccKAvx(8), + 4, + seed, + new JpegColorConverterBase.YccKScalar(8), + precísion: 4); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromYccKArm64(int seed) => + this.TestConversionToRgb(new JpegColorConverterBase.YccKArm64(8), + 4, + seed, + new JpegColorConverterBase.YccKScalar(8)); + + [Theory] + [MemberData(nameof(Seeds))] + public void FromRgbToYccKArm64(int seed) => + this.TestConversionFromRgb(new JpegColorConverterBase.YccKArm64(8), + 4, + seed, + new JpegColorConverterBase.YccKScalar(8), + precísion: 4); + + private void TestConversionToRgb( JpegColorConverterBase converter, int componentCount, - int seed) + int seed, + JpegColorConverterBase baseLineConverter = null) + { + if (!converter.IsAvailable) + { + this.Output.WriteLine( + $"Skipping test - {converter.GetType().Name} is not supported on current hardware."); + return; + } + + ValidateConversionToRgb( + converter, + componentCount, + seed, + baseLineConverter); + } + + private void TestConversionFromRgb( + JpegColorConverterBase converter, + int componentCount, + int seed, + JpegColorConverterBase baseLineConverter, + int precísion) { if (!converter.IsAvailable) { @@ -271,10 +604,12 @@ public class JpegColorConverterTests return; } - ValidateConversion( + ValidateConversionFromRgb( converter, componentCount, - seed); + seed, + baseLineConverter, + precísion); } private static JpegColorConverterBase.ComponentValues CreateRandomValues( @@ -303,24 +638,117 @@ public class JpegColorConverterTests return new JpegColorConverterBase.ComponentValues(buffers, 0); } - private static void ValidateConversion( + private static float[] CreateRandomValues(int length, Random rnd) + { + float[] values = new float[length]; + + for (int j = 0; j < values.Length; j++) + { + values[j] = (float)rnd.NextDouble() * MaxColorChannelValue; + } + + return values; + } + + private static void ValidateConversionToRgb( JpegColorConverterBase converter, int componentCount, - int seed) + int seed, + JpegColorConverterBase baseLineConverter = null) { JpegColorConverterBase.ComponentValues original = CreateRandomValues(TestBufferLength, componentCount, seed); - JpegColorConverterBase.ComponentValues values = new( + JpegColorConverterBase.ComponentValues actual = new( original.ComponentCount, original.Component0.ToArray(), original.Component1.ToArray(), original.Component2.ToArray(), original.Component3.ToArray()); - converter.ConvertToRgbInplace(values); + converter.ConvertToRgbInplace(actual); for (int i = 0; i < TestBufferLength; i++) { - Validate(converter.ColorSpace, original, values, i); + Validate(converter.ColorSpace, original, actual, i); + } + + // Compare conversion result to a baseline, should be the scalar version. + if (baseLineConverter != null) + { + JpegColorConverterBase.ComponentValues expected = new( + original.ComponentCount, + original.Component0.ToArray(), + original.Component1.ToArray(), + original.Component2.ToArray(), + original.Component3.ToArray()); + baseLineConverter.ConvertToRgbInplace(expected); + if (componentCount == 1) + { + Assert.True(expected.Component0.SequenceEqual(actual.Component0)); + } + + if (componentCount == 2) + { + Assert.True(expected.Component1.SequenceEqual(actual.Component1)); + } + + if (componentCount == 3) + { + Assert.True(expected.Component2.SequenceEqual(actual.Component2)); + } + + if (componentCount == 4) + { + Assert.True(expected.Component3.SequenceEqual(actual.Component3)); + } + } + } + + private static void ValidateConversionFromRgb( + JpegColorConverterBase converter, + int componentCount, + int seed, + JpegColorConverterBase baseLineConverter, + int precision = 4) + { + // arrange + JpegColorConverterBase.ComponentValues actual = CreateRandomValues(TestBufferLength, componentCount, seed); + JpegColorConverterBase.ComponentValues expected = CreateRandomValues(TestBufferLength, componentCount, seed); + Random rnd = new(seed); + float[] rLane = CreateRandomValues(TestBufferLength, rnd); + float[] gLane = CreateRandomValues(TestBufferLength, rnd); + float[] bLane = CreateRandomValues(TestBufferLength, rnd); + + // act + converter.ConvertFromRgb(actual, rLane, gLane, bLane); + baseLineConverter.ConvertFromRgb(expected, rLane, gLane, bLane); + + // assert + if (componentCount == 1) + { + CompareSequenceWithTolerance(expected.Component0, actual.Component0, precision); + } + + if (componentCount == 2) + { + CompareSequenceWithTolerance(expected.Component1, actual.Component1, precision); + } + + if (componentCount == 3) + { + CompareSequenceWithTolerance(expected.Component2, actual.Component2, precision); + } + + if (componentCount == 4) + { + CompareSequenceWithTolerance(expected.Component3, actual.Component3, precision); + } + } + + private static void CompareSequenceWithTolerance(Span expected, Span actual, int precision) + { + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i], actual[i], precision: precision); } } @@ -358,9 +786,9 @@ public class JpegColorConverterTests float y = values.Component0[i]; float cb = values.Component1[i]; float cr = values.Component2[i]; - var expected = ColorSpaceConverter.ToRgb(new YCbCr(y, cb, cr)); + Rgb expected = ColorSpaceConverter.ToRgb(new YCbCr(y, cb, cr)); - var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]); + Rgb actual = new(result.Component0[i], result.Component1[i], result.Component2[i]); bool equal = ColorSpaceComparer.Equals(expected, actual); Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index 9bff30b6f4..1c203e7342 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -175,20 +175,20 @@ public partial class JpegDecoderTests Assert.Equal(expectedColorType, meta.ColorType); } - private static void TestImageInfo(string imagePath, IImageDecoder decoder, bool useIdentify, Action test) + private static void TestImageInfo(string imagePath, IImageDecoder decoder, Action test) { TestFile testFile = TestFile.Create(imagePath); using MemoryStream stream = new(testFile.Bytes, false); - if (useIdentify) - { - ImageInfo imageInfo = decoder.Identify(DecoderOptions.Default, stream); - test(imageInfo); - } - else - { - using Image img = decoder.Decode(DecoderOptions.Default, stream); - test(img); - } + ImageInfo imageInfo = decoder.Identify(DecoderOptions.Default, stream); + test(imageInfo); + } + + private static void TestImageDecode(string imagePath, IImageDecoder decoder, Action test) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + using Image img = decoder.Decode(DecoderOptions.Default, stream); + test(img); } private static void TestMetadataImpl( @@ -197,25 +197,57 @@ public partial class JpegDecoderTests string imagePath, int expectedPixelSize, bool exifProfilePresent, - bool iccProfilePresent) => TestImageInfo( - imagePath, - decoder, - useIdentify, - imageInfo => + bool iccProfilePresent) + { + if (useIdentify) + { + TestImageInfo( + imagePath, + decoder, + imageInfo => { Assert.NotNull(imageInfo); Assert.NotNull(imageInfo.PixelType); + Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel); + + ExifProfile exifProfile = imageInfo.Metadata.ExifProfile; - if (useIdentify) + if (exifProfilePresent) + { + Assert.NotNull(exifProfile); + Assert.NotEmpty(exifProfile.Values); + } + else + { + Assert.Null(exifProfile); + } + + IccProfile iccProfile = imageInfo.Metadata.IccProfile; + + if (iccProfilePresent) { - Assert.Equal(expectedPixelSize, imageInfo.PixelType.BitsPerPixel); + Assert.NotNull(iccProfile); + Assert.NotEmpty(iccProfile.Entries); } else { - // When full Image decoding is performed, BitsPerPixel will match TPixel - int bpp32 = Unsafe.SizeOf() * 8; - Assert.Equal(bpp32, imageInfo.PixelType.BitsPerPixel); + Assert.Null(iccProfile); } + }); + } + else + { + TestImageDecode( + imagePath, + decoder, + imageInfo => + { + Assert.NotNull(imageInfo); + Assert.NotNull(imageInfo.PixelType); + + // When full Image decoding is performed, BitsPerPixel will match TPixel + int bpp32 = Unsafe.SizeOf() * 8; + Assert.Equal(bpp32, imageInfo.PixelType.BitsPerPixel); ExifProfile exifProfile = imageInfo.Metadata.ExifProfile; @@ -241,6 +273,8 @@ public partial class JpegDecoderTests Assert.Null(iccProfile); } }); + } + } [Theory] [InlineData(false)] @@ -268,28 +302,60 @@ public partial class JpegDecoderTests [Theory] [InlineData(false)] [InlineData(true)] - public void Decoder_Reads_Correct_Resolution_From_Jfif(bool useIdentify) => TestImageInfo( - TestImages.Jpeg.Baseline.Floorplan, - JpegDecoder.Instance, - useIdentify, - imageInfo => - { - Assert.Equal(300, imageInfo.Metadata.HorizontalResolution); - Assert.Equal(300, imageInfo.Metadata.VerticalResolution); - }); + public void Decoder_Reads_Correct_Resolution_From_Jfif(bool useIdentify) + { + if (useIdentify) + { + TestImageInfo( + TestImages.Jpeg.Baseline.Floorplan, + JpegDecoder.Instance, + imageInfo => + { + Assert.Equal(300, imageInfo.Metadata.HorizontalResolution); + Assert.Equal(300, imageInfo.Metadata.VerticalResolution); + }); + } + else + { + TestImageDecode( + TestImages.Jpeg.Baseline.Floorplan, + JpegDecoder.Instance, + image => + { + Assert.Equal(300, image.Metadata.HorizontalResolution); + Assert.Equal(300, image.Metadata.VerticalResolution); + }); + } + } [Theory] [InlineData(false)] [InlineData(true)] - public void Decoder_Reads_Correct_Resolution_From_Exif(bool useIdentify) => TestImageInfo( - TestImages.Jpeg.Baseline.Jpeg420Exif, - JpegDecoder.Instance, - useIdentify, - imageInfo => + public void Decoder_Reads_Correct_Resolution_From_Exif(bool useIdentify) + { + if (useIdentify) + { + TestImageInfo( + TestImages.Jpeg.Baseline.Jpeg420Exif, + JpegDecoder.Instance, + imageInfo => { Assert.Equal(72, imageInfo.Metadata.HorizontalResolution); Assert.Equal(72, imageInfo.Metadata.VerticalResolution); }); + } + else + { + TestImageDecode( + TestImages.Jpeg.Baseline.Jpeg420Exif, + JpegDecoder.Instance, + imageInfo => + { + Assert.Equal(72, imageInfo.Metadata.HorizontalResolution); + Assert.Equal(72, imageInfo.Metadata.VerticalResolution); + }); + } + } [Theory] [WithFile(TestImages.Jpeg.Issues.InvalidIptcTag, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs index cd93adefd8..c593a029ab 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs @@ -25,8 +25,7 @@ public partial class ReferenceImplementationsTests { float[] data = Create8x8RandomFloatData(-1000, 1000, seed); - var b0 = default(Block8x8F); - b0.LoadFrom(data); + Block8x8F b0 = Block8x8F.Load(data); Block8x8F b1 = ReferenceImplementations.AccurateDCT.TransformFDCT(ref b0); Block8x8F b2 = ReferenceImplementations.AccurateDCT.TransformIDCT(ref b1); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs index 46c6ee2da6..f5d7c159ba 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs @@ -70,8 +70,7 @@ public partial class ReferenceImplementationsTests { float[] floatData = Create8x8RandomFloatData(-1000, 1000); - Block8x8F source = default; - source.LoadFrom(floatData); + Block8x8F source = Block8x8F.Load(floatData); Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); Block8x8F actual = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformFDCT_UpscaleBy8(ref source); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs index 9e0c62d139..0d5f3114d1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs @@ -36,8 +36,7 @@ internal static partial class ReferenceImplementations float[] temp = new float[64]; IDCT2D_llm(s, d, temp); - Block8x8F result = default; - result.LoadFrom(d); + Block8x8F result = Block8x8F.Load(d); return result; } @@ -49,8 +48,7 @@ internal static partial class ReferenceImplementations float[] temp = new float[64]; FDCT2D_llm(s, d, temp); - Block8x8F result = default; - result.LoadFrom(d); + Block8x8F result = Block8x8F.Load(d); return result; } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs index e50e40bd22..796d35bf72 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs @@ -287,7 +287,7 @@ public class PngEncoderFilterTests : MeasureFixture break; case PngFilterMethod.Average: - AverageFilter.Encode(this.previousScanline, this.scanline, this.resultBuffer, this.bpp, out sum); + AverageFilter.Encode(this.previousScanline, this.scanline, this.resultBuffer, (uint)this.bpp, out sum); break; case PngFilterMethod.Paeth: diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs index 73ce216d8d..4646de7f82 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs @@ -211,8 +211,8 @@ public class BigTiffMetadataTests foreach (IExifValue entry in values) { - writer.Write((ushort)entry.Tag); - writer.Write((ushort)entry.DataType); + writer.Write((ushort)entry.Tag, buffer); + writer.Write((ushort)entry.DataType, buffer); WriteLong8(writer, buffer, ExifWriter.GetNumberOfComponents(entry)); uint length = ExifWriter.GetLength(entry); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 75e7d39091..72b87bcf02 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -12,6 +12,10 @@ using static SixLabors.ImageSharp.Tests.TestImages.Tiff; namespace SixLabors.ImageSharp.Tests.Formats.Tiff; +// Several of the tests in this class comparing images with associated alpha encoding use a high tolerance for comparison. +// This is due to an issue in the reference decoder where it is not correctly checking for a zero alpha component value +// before unpremultying the encoded values. This can lead to incorrect values when the rgb channels contain non-zero values. +// The tests should be manually verified following any changes to the decoder. [Trait("Format", "Tiff")] [ValidateDisposedMemoryAllocations] public class TiffDecoderTests : TiffDecoderBaseTester @@ -21,6 +25,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester [Theory] [WithFile(MultiframeDifferentSize, PixelTypes.Rgba32)] [WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)] + [WithFile(Cmyk64BitDeflate, PixelTypes.Rgba32)] public void ThrowsNotSupported(TestImageProvider provider) where TPixel : unmanaged, IPixel => Assert.Throws(() => provider.GetImage(TiffDecoder.Instance)); @@ -197,16 +202,8 @@ public class TiffDecoderTests : TiffDecoderBaseTester public void TiffDecoder_CanDecode_12Bit_WithAssociatedAlpha(TestImageProvider provider) where TPixel : unmanaged, IPixel { - if (TestEnvironment.IsMacOS) - { - // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder.Instance); - image.DebugSave(provider); - return; - } - // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues. - TestTiffDecoder(provider, useExactComparer: false); + TestTiffDecoder(provider, useExactComparer: false, compareTolerance: 0.264F); } [Theory] @@ -254,16 +251,8 @@ public class TiffDecoderTests : TiffDecoderBaseTester where TPixel : unmanaged, IPixel { - if (TestEnvironment.IsMacOS) - { - // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder.Instance); - image.DebugSave(provider); - return; - } - // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues. - TestTiffDecoder(provider, useExactComparer: false); + TestTiffDecoder(provider, useExactComparer: false, compareTolerance: 0.376F); } [Theory] @@ -281,16 +270,8 @@ public class TiffDecoderTests : TiffDecoderBaseTester public void TiffDecoder_CanDecode_24Bit_WithAssociatedAlpha(TestImageProvider provider) where TPixel : unmanaged, IPixel { - if (TestEnvironment.IsMacOS) - { - // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder.Instance); - image.DebugSave(provider); - return; - } - // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues. - TestTiffDecoder(provider, useExactComparer: false); + TestTiffDecoder(provider, useExactComparer: false, compareTolerance: 0.405F); } [Theory] @@ -383,13 +364,6 @@ public class TiffDecoderTests : TiffDecoderBaseTester public void TiffDecoder_CanDecode_32Bit_WithAssociatedAlpha(TestImageProvider provider) where TPixel : unmanaged, IPixel { - if (TestEnvironment.IsMacOS) - { - // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder.Instance); - image.DebugSave(provider); - return; - } // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues. TestTiffDecoder(provider, useExactComparer: false, compareTolerance: 0.004F); @@ -423,16 +397,8 @@ public class TiffDecoderTests : TiffDecoderBaseTester public void TiffDecoder_CanDecode_40Bit_WithAssociatedAlpha(TestImageProvider provider) where TPixel : unmanaged, IPixel { - if (TestEnvironment.IsMacOS) - { - // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder.Instance); - image.DebugSave(provider); - return; - } - // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues. - TestTiffDecoder(provider, useExactComparer: false); + TestTiffDecoder(provider, useExactComparer: false, compareTolerance: 0.247F); } [Theory] @@ -462,16 +428,8 @@ public class TiffDecoderTests : TiffDecoderBaseTester public void TiffDecoder_CanDecode_48Bit_WithAssociatedAlpha(TestImageProvider provider) where TPixel : unmanaged, IPixel { - if (TestEnvironment.IsMacOS) - { - // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder.Instance); - image.DebugSave(provider); - return; - } - // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues. - TestTiffDecoder(provider, useExactComparer: false, compareTolerance: 0.0002f); + TestTiffDecoder(provider, useExactComparer: false, compareTolerance: 0.118F); } [Theory] @@ -492,16 +450,8 @@ public class TiffDecoderTests : TiffDecoderBaseTester public void TiffDecoder_CanDecode_56Bit_WithAssociatedAlpha(TestImageProvider provider) where TPixel : unmanaged, IPixel { - if (TestEnvironment.IsMacOS) - { - // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though. - using Image image = provider.GetImage(TiffDecoder.Instance); - image.DebugSave(provider); - return; - } - // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues. - TestTiffDecoder(provider, useExactComparer: false, compareTolerance: 0.0002f); + TestTiffDecoder(provider, useExactComparer: false, compareTolerance: 0.075F); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index 7907597854..8724147301 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -19,7 +19,7 @@ public class TiffEncoderHeaderTests using (TiffStreamWriter writer = new(stream)) { - long firstIfdMarker = TiffEncoderCore.WriteHeader(writer); + long firstIfdMarker = TiffEncoderCore.WriteHeader(writer, stackalloc byte[4]); } Assert.Equal(new byte[] { 0x49, 0x49, 42, 0, 0x00, 0x00, 0x00, 0x00 }, stream.ToArray()); @@ -32,7 +32,7 @@ public class TiffEncoderHeaderTests TiffEncoderCore encoder = new(Encoder, Configuration.Default.MemoryAllocator); using TiffStreamWriter writer = new(stream); - long firstIfdMarker = TiffEncoderCore.WriteHeader(writer); + long firstIfdMarker = TiffEncoderCore.WriteHeader(writer, stackalloc byte[4]); Assert.Equal(4, firstIfdMarker); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 5b09a244b5..b671addf95 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -31,13 +31,16 @@ public class TiffMetadataTests TiffMetadata meta = new() { ByteOrder = ByteOrder.BigEndian, + FormatType = TiffFormatType.BigTIFF, }; TiffMetadata clone = (TiffMetadata)meta.DeepClone(); clone.ByteOrder = ByteOrder.LittleEndian; + clone.FormatType = TiffFormatType.Default; - Assert.False(meta.ByteOrder == clone.ByteOrder); + Assert.Equal(ByteOrder.BigEndian, meta.ByteOrder); + Assert.Equal(TiffFormatType.BigTIFF, meta.FormatType); } [Theory] @@ -106,6 +109,32 @@ public class TiffMetadataTests Assert.Equal(expectedByteOrder, tiffMetadata.ByteOrder); } + [Theory] + [InlineData(Cmyk, 1, TiffBitsPerPixel.Bit32, TiffPhotometricInterpretation.Separated, TiffInkSet.Cmyk)] + [InlineData(Cmyk64BitDeflate, 1, TiffBitsPerPixel.Bit64, TiffPhotometricInterpretation.Separated, TiffInkSet.Cmyk)] + [InlineData(YCbCrJpegCompressed, 1, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.YCbCr, null)] + public void Identify_Frames(string imagePath, int framesCount, TiffBitsPerPixel bitsPerPixel, TiffPhotometricInterpretation photometric, TiffInkSet? inkSet) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + + ImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata(); + Assert.NotNull(tiffMetadata); + + Assert.Equal(framesCount, imageInfo.FrameMetadataCollection.Count); + + foreach (ImageFrameMetadata metadata in imageInfo.FrameMetadataCollection) + { + TiffFrameMetadata tiffFrameMetadata = metadata.GetTiffMetadata(); + Assert.Equal(bitsPerPixel, tiffFrameMetadata.BitsPerPixel); + Assert.Equal(photometric, tiffFrameMetadata.PhotometricInterpretation); + Assert.Equal(inkSet, tiffFrameMetadata.InkSet); + } + } + [Theory] [WithFile(SampleMetadata, PixelTypes.Rgba32, false)] [WithFile(SampleMetadata, PixelTypes.Rgba32, true)] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index f6a1257f47..9b26ab2702 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -53,7 +53,7 @@ public class TiffWriterTests { using var stream = new MemoryStream(); using var writer = new TiffStreamWriter(stream); - writer.Write(1234); + writer.Write(1234, stackalloc byte[2]); Assert.Equal(new byte[] { 0xD2, 0x04 }, stream.ToArray()); } @@ -63,7 +63,7 @@ public class TiffWriterTests { using var stream = new MemoryStream(); using var writer = new TiffStreamWriter(stream); - writer.Write(12345678U); + writer.Write(12345678U, stackalloc byte[4]); Assert.Equal(new byte[] { 0x4E, 0x61, 0xBC, 0x00 }, stream.ToArray()); } @@ -89,16 +89,17 @@ public class TiffWriterTests public void WriteMarker_WritesToPlacedPosition() { using var stream = new MemoryStream(); + Span buffer = stackalloc byte[4]; using (var writer = new TiffStreamWriter(stream)) { - writer.Write(0x11111111); - long marker = writer.PlaceMarker(); - writer.Write(0x33333333); + writer.Write(0x11111111, buffer); + long marker = writer.PlaceMarker(buffer); + writer.Write(0x33333333, buffer); - writer.WriteMarker(marker, 0x12345678); + writer.WriteMarker(marker, 0x12345678, buffer); - writer.Write(0x44444444); + writer.Write(0x44444444, buffer); } Assert.Equal( diff --git a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs index 73c034a6be..4551e3e23e 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs @@ -186,21 +186,88 @@ public class LosslessUtilsTests private static void RunPredictor13Test() { // arrange - uint[] topData = { 4278193922, 4278193666 }; - const uint left = 4278193410; - const uint expectedResult = 4278193154; + uint[] topData0 = { 4278193922, 4278193666 }; + const uint left0 = 4278193410; + const uint expectedResult0 = 4278193154; + uint[] topData1 = { 4294933015, 4278219803 }; + const uint left1 = 4278236686; + const uint expectedResult1 = 4278231571; + uint actual0 = 0; + uint actual1 = 0; // act unsafe { - fixed (uint* top = &topData[1]) + fixed (uint* top = &topData0[1]) { - uint actual = LosslessUtils.Predictor13(left, top); + actual0 = LosslessUtils.Predictor13(left0, top); + } - // assert - Assert.Equal(expectedResult, actual); + fixed (uint* top = &topData1[1]) + { + actual1 = LosslessUtils.Predictor13(left1, top); } } + + // assert + Assert.Equal(expectedResult0, actual0); + Assert.Equal(expectedResult1, actual1); + } + + [Fact] + public void BundleColorMap_WithXbitsZero_Works() + { + // arrange + byte[] row = { 238, 238, 238, 238, 238, 238, 240, 237, 240, 235, 223, 223, 218, 220, 226, 219, 220, 204, 218, 211, 218, 221, 254, 255 }; + int xBits = 0; + uint[] actual = new uint[row.Length]; + uint[] expected = + { + 4278251008, 4278251008, 4278251008, 4278251008, 4278251008, + 4278251008, 4278251520, 4278250752, 4278251520, 4278250240, + 4278247168, 4278247168, 4278245888, 4278246400, 4278247936, + 4278246144, 4278246400, 4278242304, 4278245888, 4278244096, + 4278245888, 4278246656, 4278255104, 4278255360 + }; + + // act + LosslessUtils.BundleColorMap(row, actual.Length, xBits, actual); + + // assert + Assert.True(actual.SequenceEqual(expected)); + } + + [Fact] + public void BundleColorMap_WithXbitsNoneZero_Works() + { + // arrange + byte[] row = + { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 + }; + int xBits = 2; + uint[] actual = new uint[row.Length]; + uint[] expected = + { + 4278233600, 4278233600, 4278233600, 4278233600, 4278255360, 4278255360, 4278255360, 4278255360, 4278233600, 4278233600, 4278233600, 4278233600, + 4278255360, 4278255360, 4278255360, 4278255360, 4278211840, 4278211840, 4278211840, 4278211840, 4278255360, 4278255360, 4278255360, 4278255360, + 4278255360, 4278255360, 4278255360, 4278255360, 4278255360, 4278255360, 4278255360, 4278206208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + // act + LosslessUtils.BundleColorMap(row, actual.Length, xBits, actual); + + // assert + Assert.True(actual.SequenceEqual(expected)); } [Fact] @@ -215,9 +282,6 @@ public class LosslessUtilsTests [Fact] public void Predictor13_Works() => RunPredictor13Test(); - [Fact] - public void SubtractGreen_Works() => RunSubtractGreenTest(); - [Fact] public void AddGreenToBlueAndRed_Works() => RunAddGreenToBlueAndRedTest(); @@ -251,12 +315,18 @@ public class LosslessUtilsTests [Fact] public void Predictor13_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor13Test, HwIntrinsics.DisableSSE2); + [Fact] + public void SubtractGreen_Works() => RunSubtractGreenTest(); + [Fact] public void SubtractGreen_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.AllowAll); [Fact] public void SubtractGreen_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.DisableAVX2); + [Fact] + public void SubtractGreen_Scalar_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.DisableHWIntrinsic); + [Fact] public void SubtractGreen_WithoutAvxOrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSSE3); diff --git a/tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs index 69b503b5e5..73e7044f5b 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Webp.Lossy; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -77,151 +78,73 @@ public class LossyUtilsTests private static void RunVp8Sse16X16Test() { // arrange - byte[] a = + Random rand = new(1234); + byte[] a = new byte[512 * 10]; + byte[] b = new byte[512 * 10]; + for (int i = 0; i < a.Length; i++) { - 154, 154, 151, 151, 149, 148, 151, 157, 163, 163, 154, 132, 102, 98, 104, 108, 107, 104, 104, 103, - 101, 106, 123, 119, 170, 171, 172, 171, 168, 175, 171, 173, 151, 151, 149, 150, 147, 147, 146, 159, - 164, 165, 154, 129, 92, 90, 101, 105, 104, 103, 104, 101, 100, 105, 123, 117, 172, 172, 172, 168, - 170, 177, 170, 175, 151, 149, 150, 150, 147, 147, 156, 161, 161, 161, 151, 126, 93, 90, 102, 107, - 104, 103, 104, 101, 104, 104, 122, 117, 172, 172, 170, 168, 170, 177, 172, 175, 150, 149, 152, 151, - 148, 151, 160, 159, 157, 157, 148, 133, 96, 90, 103, 107, 104, 104, 101, 100, 102, 102, 121, 117, - 170, 170, 169, 171, 171, 179, 173, 175, 149, 151, 152, 151, 148, 154, 162, 157, 154, 154, 151, 132, - 92, 89, 101, 108, 104, 102, 101, 101, 103, 103, 123, 118, 171, 168, 177, 173, 171, 178, 172, 176, - 152, 152, 152, 151, 154, 162, 161, 155, 149, 157, 156, 129, 92, 87, 101, 107, 102, 100, 107, 100, - 101, 102, 123, 118, 170, 175, 182, 172, 171, 179, 173, 175, 152, 151, 154, 155, 160, 162, 161, 153, - 150, 156, 153, 129, 92, 91, 102, 106, 100, 109, 115, 99, 101, 102, 124, 120, 171, 179, 178, 172, - 171, 181, 171, 173, 154, 154, 154, 162, 160, 158, 156, 152, 153, 157, 151, 128, 86, 86, 102, 105, - 102, 122, 114, 99, 101, 102, 125, 120, 178, 173, 177, 172, 171, 180, 172, 173, 154, 152, 158, 163, - 150, 148, 148, 156, 151, 158, 152, 129, 87, 87, 101, 105, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 154, 151, 165, 156, 141, 137, 146, 158, 152, 159, 152, 133, - 90, 88, 99, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 154, 160, 164, 150, 126, 127, 149, 159, 155, 161, 153, 131, 84, 86, 97, 103, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 157, 167, 157, 137, 102, 128, 155, 161, - 157, 159, 154, 134, 84, 82, 97, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 163, 163, 150, 113, 78, 132, 156, 162, 159, 160, 154, 132, 83, 78, 91, 97, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 163, 157, 137, 80, 78, - 131, 154, 163, 157, 159, 149, 131, 82, 77, 94, 100, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 159, 151, 108, 72, 88, 132, 156, 162, 159, 157, 151, 130, 79, 78, - 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 151, 130, - 82, 82, 89, 134, 154, 161, 161, 157, 152, 129, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204 - }; - - byte[] b = + a[i] = (byte)rand.Next(byte.MaxValue); + b[i] = (byte)rand.Next(byte.MaxValue); + } + int[] expected = { 2533110, 2818581, 2984663, 2891188, 2855134, 2634604, 2466504, 3061747, 2626010, 2640965 }; + + // act + assert + int offset = 0; + for (int i = 0; i < expected.Length; i++) { - 150, 150, 150, 150, 146, 149, 152, 154, 164, 166, 154, 132, 99, 92, 106, 112, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 150, 150, 150, 150, 146, 149, 152, 154, - 161, 164, 151, 130, 93, 86, 100, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 150, 150, 150, 150, 146, 149, 152, 154, 158, 161, 148, 127, 93, 86, 100, 106, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 150, 150, 150, 150, - 146, 149, 152, 154, 156, 159, 146, 125, 99, 92, 106, 112, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 148, 148, 148, 148, 149, 158, 162, 159, 155, 155, 153, 129, - 94, 87, 101, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 151, 151, 151, 151, 152, 159, 161, 156, 155, 155, 153, 129, 94, 87, 101, 106, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 154, 154, 154, 154, 156, 161, 159, 152, - 155, 155, 153, 129, 94, 87, 101, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 156, 156, 156, 156, 159, 162, 158, 149, 155, 155, 153, 129, 94, 87, 101, 106, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 152, 153, 157, 162, - 150, 149, 149, 151, 155, 160, 150, 131, 91, 90, 104, 104, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 152, 156, 158, 157, 140, 137, 145, 159, 155, 160, 150, 131, - 89, 88, 102, 101, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 153, 161, 160, 149, 118, 128, 147, 162, 155, 160, 150, 131, 86, 85, 99, 98, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 154, 165, 161, 144, 96, 128, 154, 159, 155, - 160, 150, 131, 83, 82, 97, 96, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 161, 160, 149, 105, 78, 127, 156, 170, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 160, 160, 133, 85, 81, 129, 155, - 167, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 156, 147, 109, 76, 85, 130, 153, 163, 156, 156, 154, 130, 81, 77, 95, 102, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 152, 128, 87, 83, - 88, 132, 152, 159, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204 - }; - - int expected = 2063; + int actual = LossyUtils.Vp8_Sse16x16(a.AsSpan(offset), b.AsSpan(offset)); + Assert.Equal(expected[i], actual); - // act - int actual = LossyUtils.Vp8_Sse16X16(a, b); - - // assert - Assert.Equal(expected, actual); + offset += 512; + } } private static void RunVp8Sse16X8Test() { // arrange - byte[] a = + Random rand = new(1234); + byte[] a = new byte[256 * 10]; + byte[] b = new byte[256 * 10]; + for (int i = 0; i < a.Length; i++) { - 107, 104, 104, 103, 101, 106, 123, 119, 170, 171, 172, 171, 168, 175, 171, 173, 151, 151, 149, 150, - 147, 147, 146, 159, 164, 165, 154, 129, 92, 90, 101, 105, 104, 103, 104, 101, 100, 105, 123, 117, - 172, 172, 172, 168, 170, 177, 170, 175, 151, 149, 150, 150, 147, 147, 156, 161, 161, 161, 151, 126, - 93, 90, 102, 107, 104, 103, 104, 101, 104, 104, 122, 117, 172, 172, 170, 168, 170, 177, 172, 175, - 150, 149, 152, 151, 148, 151, 160, 159, 157, 157, 148, 133, 96, 90, 103, 107, 104, 104, 101, 100, - 102, 102, 121, 117, 170, 170, 169, 171, 171, 179, 173, 175, 149, 151, 152, 151, 148, 154, 162, 157, - 154, 154, 151, 132, 92, 89, 101, 108, 104, 102, 101, 101, 103, 103, 123, 118, 171, 168, 177, 173, - 171, 178, 172, 176, 152, 152, 152, 151, 154, 162, 161, 155, 149, 157, 156, 129, 92, 87, 101, 107, - 102, 100, 107, 100, 101, 102, 123, 118, 170, 175, 182, 172, 171, 179, 173, 175, 152, 151, 154, 155, - 160, 162, 161, 153, 150, 156, 153, 129, 92, 91, 102, 106, 100, 109, 115, 99, 101, 102, 124, 120, - 171, 179, 178, 172, 171, 181, 171, 173, 154, 154, 154, 162, 160, 158, 156, 152, 153, 157, 151, 128, - 86, 86, 102, 105, 102, 122, 114, 99, 101, 102, 125, 120, 178, 173, 177, 172, 171, 180, 172, 173, - 154, 152, 158, 163, 150, 148, 148, 156, 151, 158, 152, 129, 87, 87, 101, 105 - }; - - byte[] b = + a[i] = (byte)rand.Next(byte.MaxValue); + b[i] = (byte)rand.Next(byte.MaxValue); + } + int[] expected = { 1298274, 1234836, 1325264, 1493317, 1551995, 1432668, 1407891, 1483297, 1537930, 1317204 }; + + // act + assert + int offset = 0; + for (int i = 0; i < expected.Length; i++) { - 103, 103, 103, 103, 101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175, 150, 150, 150, 150, - 146, 149, 152, 154, 161, 164, 151, 130, 93, 86, 100, 106, 103, 103, 103, 103, 101, 106, 122, 114, - 171, 171, 171, 171, 171, 177, 169, 175, 150, 150, 150, 150, 146, 149, 152, 154, 158, 161, 148, 127, - 93, 86, 100, 106, 103, 103, 103, 103, 101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175, - 150, 150, 150, 150, 146, 149, 152, 154, 156, 159, 146, 125, 99, 92, 106, 112, 103, 103, 103, 103, - 101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175, 148, 148, 148, 148, 149, 158, 162, 159, - 155, 155, 153, 129, 94, 87, 101, 106, 102, 100, 100, 102, 100, 101, 120, 122, 170, 176, 176, 170, - 174, 180, 171, 177, 151, 151, 151, 151, 152, 159, 161, 156, 155, 155, 153, 129, 94, 87, 101, 106, - 102, 105, 105, 102, 100, 101, 120, 122, 170, 176, 176, 170, 174, 180, 171, 177, 154, 154, 154, 154, - 156, 161, 159, 152, 155, 155, 153, 129, 94, 87, 101, 106, 102, 112, 112, 102, 100, 101, 120, 122, - 170, 176, 176, 170, 174, 180, 171, 177, 156, 156, 156, 156, 159, 162, 158, 149, 155, 155, 153, 129, - 94, 87, 101, 106, 102, 117, 117, 102, 100, 101, 120, 122, 170, 176, 176, 170, 174, 180, 171, 177, - 152, 153, 157, 162, 150, 149, 149, 151, 155, 160, 150, 131, 91, 90, 104, 104 - }; - - int expected = 749; - - // act - int actual = LossyUtils.Vp8_Sse16X8(a, b); + int actual = LossyUtils.Vp8_Sse16x8(a.AsSpan(offset), b.AsSpan(offset)); + Assert.Equal(expected[i], actual); - // assert - Assert.Equal(expected, actual); + offset += 256; + } } private static void RunVp8Sse4X4Test() { // arrange - byte[] a = + Random rand = new(1234); + byte[] a = new byte[128 * 10]; + byte[] b = new byte[128 * 10]; + for (int i = 0; i < a.Length; i++) { - 27, 27, 28, 29, 29, 28, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, - 129, 128, 128, 128, 128, 128, 128, 128, 128, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 29, 29, 28, - 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 27, 27, 26, - 26, 26, 26, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, - 128, 128, 128, 128, 128, 128, 128, 28, 27, 27, 26, 26, 27, 27, 28, 27, 28, 28, 29, 29, 28, 28, 27, - 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128 - }; - - byte[] b = + a[i] = (byte)rand.Next(byte.MaxValue); + b[i] = (byte)rand.Next(byte.MaxValue); + } + int[] expected = { 194133, 125861, 165966, 195688, 106491, 173015, 266960, 200272, 311224, 122545 }; + + // act + assert + int offset = 0; + for (int i = 0; i < expected.Length; i++) { - 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 26, 26, 26, - 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204 - }; + int actual = LossyUtils.Vp8_Sse4x4(a.AsSpan(offset), b.AsSpan(offset)); + Assert.Equal(expected[i], actual); - int expected = 27; - - // act - int actual = LossyUtils.Vp8_Sse4X4(a, b); - - // assert - Assert.Equal(expected, actual); + offset += 128; + } } private static void RunMean16x4Test() @@ -300,53 +223,99 @@ public class LossyUtilsTests public void HadamardTransform_Works() => RunHadamardTransformTest(); [Fact] - public void TransformTwo_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformTwoTest, HwIntrinsics.AllowAll); + public void TransformTwo_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformTwoTest, HwIntrinsics.AllowAll); [Fact] - public void TransformTwo_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformTwoTest, HwIntrinsics.DisableHWIntrinsic); + public void TransformTwo_WithoutHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformTwoTest, HwIntrinsics.DisableHWIntrinsic); [Fact] - public void TransformOne_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformOneTest, HwIntrinsics.AllowAll); + public void TransformOne_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformOneTest, HwIntrinsics.AllowAll); [Fact] - public void TransformOne_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformOneTest, HwIntrinsics.DisableHWIntrinsic); + public void TransformOne_WithoutHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformOneTest, HwIntrinsics.DisableHWIntrinsic); + // This will test the AVX2 or ARM version. [Fact] - public void Vp8Sse16X16_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.AllowAll); + public void Vp8Sse16X16_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.AllowAll); + // This will test the SSE2 version. [Fact] - public void Vp8Sse16X16_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.DisableSSE2); + public void Vp8Sse16X16_WithoutAVX2_Works() + { + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + return; + } + + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.DisableAVX2); + } + // This will test the fallback scalar version. [Fact] - public void Vp8Sse16X16_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.DisableAVX2); + public void Vp8Sse16X16_WithoutHwIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.DisableHWIntrinsic); + // This will test the AVX2 or ARM version. [Fact] - public void Vp8Sse16X8_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.AllowAll); + public void Vp8Sse16X8_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.AllowAll); + // This will test the SSE2 version. [Fact] - public void Vp8Sse16X8_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.DisableSSE2); + public void Vp8Sse16X8_WithoutAVX2_Works() + { + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + return; + } + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.DisableAVX2); + } + + // This will test the fallback scalar version. [Fact] - public void Vp8Sse16X8_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.DisableAVX2); + public void Vp8Sse16X8_WithoutHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.DisableHWIntrinsic); + // This will test the AVX2 version or ARM version. [Fact] - public void Vp8Sse4X4_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.AllowAll); + public void Vp8Sse4X4_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.AllowAll); + // This will test the SSE2 version. [Fact] - public void Vp8Sse4X4_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.DisableSSE2); + public void Vp8Sse4X4_WithoutAVX2_Works() + { + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + return; + } + + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.DisableAVX2); + } + // This will test the fallback scalar version. [Fact] - public void Vp8Sse4X4_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.DisableAVX2); + public void Vp8Sse4X4_WithoutHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.DisableHWIntrinsic); [Fact] - public void Mean16x4_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunMean16x4Test, HwIntrinsics.AllowAll); + public void Mean16x4_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunMean16x4Test, HwIntrinsics.AllowAll); [Fact] - public void Mean16x4_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunMean16x4Test, HwIntrinsics.DisableHWIntrinsic); + public void Mean16x4_WithoutHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunMean16x4Test, HwIntrinsics.DisableHWIntrinsic); [Fact] - public void HadamardTransform_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunHadamardTransformTest, HwIntrinsics.AllowAll); + public void HadamardTransform_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunHadamardTransformTest, HwIntrinsics.AllowAll); [Fact] - public void HadamardTransform_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunHadamardTransformTest, HwIntrinsics.DisableHWIntrinsic); + public void HadamardTransform_WithoutHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunHadamardTransformTest, HwIntrinsics.DisableHWIntrinsic); } diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs index fba19065b0..62d1ef4db9 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs @@ -182,12 +182,12 @@ public abstract partial class ImageFrameCollectionTests new[] { imageFrame1, imageFrame2 }); IPixelSource[] framesSnapShot = collection.OfType>().ToArray(); + + Assert.All(framesSnapShot, f => Assert.False(f.PixelBuffer.IsDisposed)); + collection.Dispose(); - Assert.All( - framesSnapShot, - f => // The pixel source of the frame is null after its been disposed. - Assert.Null(f.PixelBuffer)); + Assert.All(framesSnapShot, f => Assert.True(f.PixelBuffer.IsDisposed)); } [Theory] diff --git a/tests/ImageSharp.Tests/ImageInfoTests.cs b/tests/ImageSharp.Tests/ImageInfoTests.cs index 39091281d3..73324eccd7 100644 --- a/tests/ImageSharp.Tests/ImageInfoTests.cs +++ b/tests/ImageSharp.Tests/ImageInfoTests.cs @@ -18,7 +18,7 @@ public class ImageInfoTests PixelTypeInfo pixelType = new(8); ImageMetadata meta = new(); - ImageInfo info = new(pixelType, width, height, meta); + ImageInfo info = new(pixelType, size, meta); Assert.Equal(pixelType, info.PixelType); Assert.Equal(width, info.Width); @@ -27,4 +27,26 @@ public class ImageInfoTests Assert.Equal(rectangle, info.Bounds); Assert.Equal(meta, info.Metadata); } + + [Fact] + public void ImageInfoInitializesCorrectlyWithFrameMetadata() + { + const int width = 50; + const int height = 60; + Size size = new(width, height); + Rectangle rectangle = new(0, 0, width, height); + PixelTypeInfo pixelType = new(8); + ImageMetadata meta = new(); + IReadOnlyList frameMetadata = new List() { new() }; + + ImageInfo info = new(pixelType, size, meta, frameMetadata); + + Assert.Equal(pixelType, info.PixelType); + Assert.Equal(width, info.Width); + Assert.Equal(height, info.Height); + Assert.Equal(size, info.Size); + Assert.Equal(rectangle, info.Bounds); + Assert.Equal(meta, info.Metadata); + Assert.Equal(frameMetadata.Count, info.FrameMetadataCollection.Count); + } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs index c81b0a74ff..1086afe76d 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -1,59 +1,66 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders; - using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities; -using Xunit; +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders; public class PorterDuffCompositorTests { // TODO: Add other modes to compare. public static readonly TheoryData CompositingOperators = - new TheoryData - { - PixelAlphaCompositionMode.Src, - PixelAlphaCompositionMode.SrcAtop, - PixelAlphaCompositionMode.SrcOver, - PixelAlphaCompositionMode.SrcIn, - PixelAlphaCompositionMode.SrcOut, - PixelAlphaCompositionMode.Dest, - PixelAlphaCompositionMode.DestAtop, - PixelAlphaCompositionMode.DestOver, - PixelAlphaCompositionMode.DestIn, - PixelAlphaCompositionMode.DestOut, - PixelAlphaCompositionMode.Clear, - PixelAlphaCompositionMode.Xor - }; + new() + { + PixelAlphaCompositionMode.Src, + PixelAlphaCompositionMode.SrcAtop, + PixelAlphaCompositionMode.SrcOver, + PixelAlphaCompositionMode.SrcIn, + PixelAlphaCompositionMode.SrcOut, + PixelAlphaCompositionMode.Dest, + PixelAlphaCompositionMode.DestAtop, + PixelAlphaCompositionMode.DestOver, + PixelAlphaCompositionMode.DestIn, + PixelAlphaCompositionMode.DestOut, + PixelAlphaCompositionMode.Clear, + PixelAlphaCompositionMode.Xor + }; [Theory] [WithFile(TestImages.Png.PDDest, nameof(CompositingOperators), PixelTypes.Rgba32)] public void PorterDuffOutputIsCorrect(TestImageProvider provider, PixelAlphaCompositionMode mode) { - var srcFile = TestFile.Create(TestImages.Png.PDSrc); - using (Image src = srcFile.CreateRgba32Image()) - using (Image dest = provider.GetImage()) + static void RunTest(string providerDump, string alphaMode) { - var options = new GraphicsOptions + TestImageProvider provider + = BasicSerializer.Deserialize>(providerDump); + + TestFile srcFile = TestFile.Create(TestImages.Png.PDSrc); + using Image src = srcFile.CreateRgba32Image(); + using Image dest = provider.GetImage(); + GraphicsOptions options = new() { Antialias = false, - AlphaCompositionMode = mode + AlphaCompositionMode = Enum.Parse(alphaMode) }; - using (Image res = dest.Clone(x => x.DrawImage(src, options))) - { - string combinedMode = mode.ToString(); - - if (combinedMode != "Src" && combinedMode.StartsWith("Src")) - { - combinedMode = combinedMode.Substring(3); - } + using Image res = dest.Clone(x => x.DrawImage(src, options)); + string combinedMode = alphaMode; - res.DebugSave(provider, combinedMode); - res.CompareToReferenceOutput(provider, combinedMode); + if (combinedMode != "Src" && combinedMode.StartsWith("Src", StringComparison.OrdinalIgnoreCase)) + { + combinedMode = combinedMode[3..]; } + + res.DebugSave(provider, combinedMode); + res.CompareToReferenceOutput(provider, combinedMode); } + + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunTest, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX, + provider, + mode.ToString()); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs index 45dece8ec8..976a272ebf 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs @@ -2,6 +2,9 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using Castle.Components.DictionaryAdapter; using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -9,7 +12,9 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders; public class PorterDuffFunctionsTests { - public static TheoryData NormalBlendFunctionData = new TheoryData + private static readonly ApproximateFloatComparer FloatComparer = new(.000001F); + + public static TheoryData NormalBlendFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, { new TestVector4(1, 1, 1, 1), new TestVector4(0, 0, 0, .8f), .5f, new TestVector4(0.6f, 0.6f, 0.6f, 1) } @@ -23,7 +28,24 @@ public class PorterDuffFunctionsTests Assert.Equal(expected, actual); } - public static TheoryData MultiplyFunctionData = new TheoryData + [Theory] + [MemberData(nameof(NormalBlendFunctionData))] + public void NormalBlendFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx.IsSupported) + { + return; + } + + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); + Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + + Vector256 expected256 = Vector256.Create(expected.X, expected.Y, expected.Z, expected.W, expected.X, expected.Y, expected.Z, expected.W); + Vector256 actual = PorterDuffFunctions.NormalSrcOver(back256, source256, Vector256.Create(amount)); + Assert.Equal(expected256, actual, FloatComparer); + } + + public static TheoryData MultiplyFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, { new TestVector4(1, 1, 1, 1), new TestVector4(0, 0, 0, .8f), .5f, new TestVector4(0.6f, 0.6f, 0.6f, 1) }, @@ -38,22 +60,56 @@ public class PorterDuffFunctionsTests VectorAssert.Equal(expected, actual, 5); } - public static TheoryData AddFunctionData = new TheoryData + [Theory] + [MemberData(nameof(MultiplyFunctionData))] + public void MultiplyFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx.IsSupported) + { + return; + } + + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); + Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + + Vector256 expected256 = Vector256.Create(expected.X, expected.Y, expected.Z, expected.W, expected.X, expected.Y, expected.Z, expected.W); + Vector256 actual = PorterDuffFunctions.MultiplySrcOver(back256, source256, Vector256.Create(amount)); + Assert.Equal(expected256, actual, FloatComparer); + } + + public static TheoryData AddFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, - { new TestVector4(1, 1, 1, 1), new TestVector4(0, 0, 0, .8f), .5f, new TestVector4(.6f, .6f, .6f, 1f) }, - { new TestVector4(0.2f, 0.2f, 0.2f, 0.3f), new TestVector4(0.3f, 0.3f, 0.3f, 0.2f), .5f, new TestVector4(.2075676f, .2075676f, .2075676f, .37f) } + { new TestVector4(1, 1, 1, 1), new TestVector4(0, 0, 0, .8f), .5f, new TestVector4(1, 1, 1, 1) }, + { new TestVector4(0.2f, 0.2f, 0.2f, 0.3f), new TestVector4(0.3f, 0.3f, 0.3f, 0.2f), .5f, new TestVector4(0.24324325f, 0.24324325f, 0.24324325f, .37f) } }; [Theory] [MemberData(nameof(AddFunctionData))] public void AddFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.MultiplySrcOver((Vector4)back, source, amount); + Vector4 actual = PorterDuffFunctions.AddSrcOver((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } - public static TheoryData SubtractFunctionData = new TheoryData + [Theory] + [MemberData(nameof(AddFunctionData))] + public void AddFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx.IsSupported) + { + return; + } + + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); + Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + + Vector256 expected256 = Vector256.Create(expected.X, expected.Y, expected.Z, expected.W, expected.X, expected.Y, expected.Z, expected.W); + Vector256 actual = PorterDuffFunctions.AddSrcOver(back256, source256, Vector256.Create(amount)); + Assert.Equal(expected256, actual, FloatComparer); + } + + public static TheoryData SubtractFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(0, 0, 0, 1) }, { new TestVector4(1, 1, 1, 1), new TestVector4(0, 0, 0, .8f), .5f, new TestVector4(1, 1, 1, 1f) }, @@ -68,7 +124,24 @@ public class PorterDuffFunctionsTests VectorAssert.Equal(expected, actual, 5); } - public static TheoryData ScreenFunctionData = new TheoryData + [Theory] + [MemberData(nameof(SubtractFunctionData))] + public void SubtractFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx.IsSupported) + { + return; + } + + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); + Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + + Vector256 expected256 = Vector256.Create(expected.X, expected.Y, expected.Z, expected.W, expected.X, expected.Y, expected.Z, expected.W); + Vector256 actual = PorterDuffFunctions.SubtractSrcOver(back256, source256, Vector256.Create(amount)); + Assert.Equal(expected256, actual, FloatComparer); + } + + public static TheoryData ScreenFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, { new TestVector4(1, 1, 1, 1), new TestVector4(0, 0, 0, .8f), .5f, new TestVector4(1, 1, 1, 1f) }, @@ -83,7 +156,24 @@ public class PorterDuffFunctionsTests VectorAssert.Equal(expected, actual, 5); } - public static TheoryData DarkenFunctionData = new TheoryData + [Theory] + [MemberData(nameof(ScreenFunctionData))] + public void ScreenFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx.IsSupported) + { + return; + } + + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); + Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + + Vector256 expected256 = Vector256.Create(expected.X, expected.Y, expected.Z, expected.W, expected.X, expected.Y, expected.Z, expected.W); + Vector256 actual = PorterDuffFunctions.ScreenSrcOver(back256, source256, Vector256.Create(amount)); + Assert.Equal(expected256, actual, FloatComparer); + } + + public static TheoryData DarkenFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, { new TestVector4(1, 1, 1, 1), new TestVector4(0, 0, 0, .8f), .5f, new TestVector4(.6f, .6f, .6f, 1f) }, @@ -98,7 +188,24 @@ public class PorterDuffFunctionsTests VectorAssert.Equal(expected, actual, 5); } - public static TheoryData LightenFunctionData = new TheoryData + [Theory] + [MemberData(nameof(DarkenFunctionData))] + public void DarkenFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx.IsSupported) + { + return; + } + + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); + Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + + Vector256 expected256 = Vector256.Create(expected.X, expected.Y, expected.Z, expected.W, expected.X, expected.Y, expected.Z, expected.W); + Vector256 actual = PorterDuffFunctions.DarkenSrcOver(back256, source256, Vector256.Create(amount)); + Assert.Equal(expected256, actual, FloatComparer); + } + + public static TheoryData LightenFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, { new TestVector4(1, 1, 1, 1), new TestVector4(0, 0, 0, .8f), .5f, new TestVector4(1, 1, 1, 1f) }, @@ -113,7 +220,24 @@ public class PorterDuffFunctionsTests VectorAssert.Equal(expected, actual, 5); } - public static TheoryData OverlayFunctionData = new TheoryData + [Theory] + [MemberData(nameof(LightenFunctionData))] + public void LightenFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx.IsSupported) + { + return; + } + + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); + Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + + Vector256 expected256 = Vector256.Create(expected.X, expected.Y, expected.Z, expected.W, expected.X, expected.Y, expected.Z, expected.W); + Vector256 actual = PorterDuffFunctions.LightenSrcOver(back256, source256, Vector256.Create(amount)); + Assert.Equal(expected256, actual, FloatComparer); + } + + public static TheoryData OverlayFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, { new TestVector4(1, 1, 1, 1), new TestVector4(0, 0, 0, .8f), .5f, new TestVector4(1, 1, 1, 1f) }, @@ -128,7 +252,24 @@ public class PorterDuffFunctionsTests VectorAssert.Equal(expected, actual, 5); } - public static TheoryData HardLightFunctionData = new TheoryData + [Theory] + [MemberData(nameof(OverlayFunctionData))] + public void OverlayFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx.IsSupported) + { + return; + } + + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); + Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + + Vector256 expected256 = Vector256.Create(expected.X, expected.Y, expected.Z, expected.W, expected.X, expected.Y, expected.Z, expected.W); + Vector256 actual = PorterDuffFunctions.OverlaySrcOver(back256, source256, Vector256.Create(amount)); + Assert.Equal(expected256, actual, FloatComparer); + } + + public static TheoryData HardLightFunctionData { get; } = new() { { new TestVector4(1, 1, 1, 1), new TestVector4(1, 1, 1, 1), 1, new TestVector4(1, 1, 1, 1) }, { new TestVector4(1, 1, 1, 1), new TestVector4(0, 0, 0, .8f), .5f, new TestVector4(0.6f, 0.6f, 0.6f, 1f) }, @@ -142,4 +283,21 @@ public class PorterDuffFunctionsTests Vector4 actual = PorterDuffFunctions.HardLightSrcOver((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } + + [Theory] + [MemberData(nameof(HardLightFunctionData))] + public void HardLightFunction256(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + if (!Avx.IsSupported) + { + return; + } + + Vector256 back256 = Vector256.Create(back.X, back.Y, back.Z, back.W, back.X, back.Y, back.Z, back.W); + Vector256 source256 = Vector256.Create(source.X, source.Y, source.Z, source.W, source.X, source.Y, source.Z, source.W); + + Vector256 expected256 = Vector256.Create(expected.X, expected.Y, expected.Z, expected.W, expected.X, expected.Y, expected.Z, expected.W); + Vector256 actual = PorterDuffFunctions.HardLightSrcOver(back256, source256, Vector256.Create(amount)); + Assert.Equal(expected256, actual, FloatComparer); + } } diff --git a/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs index 94309d2008..9c0e83a7fe 100644 --- a/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs +++ b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Globalization; +using System.Numerics; using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Tests.Primitives; @@ -115,27 +116,21 @@ public class ColorMatrixTests public void ColorMatrixHashCode() { ColorMatrix m = KnownFilterMatrices.CreateBrightnessFilter(.5F); + HashCode hash = default; - hash.Add(m.M11); - hash.Add(m.M12); - hash.Add(m.M13); - hash.Add(m.M14); - hash.Add(m.M21); - hash.Add(m.M22); - hash.Add(m.M23); - hash.Add(m.M24); - hash.Add(m.M31); - hash.Add(m.M32); - hash.Add(m.M33); - hash.Add(m.M34); - hash.Add(m.M41); - hash.Add(m.M42); - hash.Add(m.M43); - hash.Add(m.M44); - hash.Add(m.M51); - hash.Add(m.M52); - hash.Add(m.M53); - hash.Add(m.M54); + + Vector4 x = new(m.M11, m.M12, m.M13, m.M14); + Vector4 y = new(m.M21, m.M22, m.M23, m.M24); + Vector4 z = new(m.M31, m.M32, m.M33, m.M34); + Vector4 w = new(m.M41, m.M42, m.M43, m.M44); + Vector4 v = new(m.M51, m.M52, m.M53, m.M54); + + hash.Add(x); + hash.Add(y); + hash.Add(z); + hash.Add(w); + hash.Add(v); + Assert.Equal(hash.ToHashCode(), m.GetHashCode()); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs index 3cea431fbb..c6da46ee2f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs @@ -128,7 +128,7 @@ public partial class ResizeKernelMapTests for (int i = 0; i < kernelMap.DestinationLength; i++) { - ResizeKernel kernel = kernelMap.GetKernel(i); + ResizeKernel kernel = kernelMap.GetKernel((uint)i); ReferenceKernel referenceKernel = referenceMap.GetKernel(i); @@ -153,7 +153,7 @@ public partial class ResizeKernelMapTests } private static string PrintKernelMap(ResizeKernelMap kernelMap) - => PrintKernelMap(kernelMap, km => km.DestinationLength, (km, i) => km.GetKernel(i)); + => PrintKernelMap(kernelMap, km => km.DestinationLength, (km, i) => km.GetKernel((uint)i)); private static string PrintKernelMap(ReferenceKernelMap kernelMap) => PrintKernelMap(kernelMap, km => km.DestinationSize, (km, i) => km.GetKernel(i)); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index d8ce88dc3b..d1b005ee4e 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -31,7 +31,7 @@ public class ResizeTests }; private static readonly ImageComparer ValidatorComparer = - ImageComparer.TolerantPercentage(TestEnvironment.IsMacOS && TestEnvironment.RunsOnCI ? 0.26F : 0.07F); + ImageComparer.TolerantPercentage(0.07F); [Fact] public void Resize_PixelAgnostic() diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 92dd791328..f597b708d5 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -203,7 +204,9 @@ public class TestFormat : IImageFormatConfigurationModule, IImageFormat { Image image = this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken); - return new(image.PixelType, image.Width, image.Height, image.Metadata); + ImageFrameCollection m = image.Frames; + + return new(image.PixelType, image.Size, image.Metadata, new List(image.Frames.Select(x => x.Metadata))); } protected override TestDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 7ba85ce2ae..c21c59a5ca 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -968,6 +968,7 @@ public static class TestImages public const string CieLabLzwPredictor = "Tiff/CieLab_lzwcompressed_predictor.tiff"; public const string Cmyk = "Tiff/Cmyk.tiff"; + public const string Cmyk64BitDeflate = "Tiff/cmyk_deflate_64bit.tiff"; public const string Issues1716Rgb161616BitLittleEndian = "Tiff/Issues/Issue1716.tiff"; public const string Issues1891 = "Tiff/Issues/Issue1891.tiff"; diff --git a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs index 6d9652d898..e35f36feec 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using System.Numerics; +using System.Runtime.Intrinsics; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests; @@ -14,7 +16,8 @@ internal readonly struct ApproximateFloatComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer> { private readonly float epsilon; @@ -72,4 +75,16 @@ internal readonly struct ApproximateFloatComparer : /// public int GetHashCode(ColorMatrix obj) => obj.GetHashCode(); + + public bool Equals(Vector256 x, Vector256 y) + => this.Equals(x.GetElement(0), y.GetElement(0)) + && this.Equals(x.GetElement(1), y.GetElement(1)) + && this.Equals(x.GetElement(2), y.GetElement(2)) + && this.Equals(x.GetElement(3), y.GetElement(3)) + && this.Equals(x.GetElement(4), y.GetElement(4)) + && this.Equals(x.GetElement(5), y.GetElement(5)) + && this.Equals(x.GetElement(6), y.GetElement(6)) + && this.Equals(x.GetElement(7), y.GetElement(7)); + + public int GetHashCode([DisallowNull] Vector256 obj) => obj.GetHashCode(); } diff --git a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs index 1bb64d99d6..5a9a72f967 100644 --- a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs +++ b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs @@ -62,7 +62,7 @@ public static class FeatureTestRunner ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { - processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; + processStartInfo.Environment[$"DOTNET_{intrinsic.Value}"] = "0"; RemoteExecutor.Invoke( action, @@ -257,6 +257,52 @@ public static class FeatureTestRunner } } + /// + /// Runs the given test within an environment + /// where the given features. + /// + /// The test action to run. + /// The intrinsics features. + /// The value to pass as a parameter to the test action. + /// The second value to pass as a parameter to the test action. + public static void RunWithHwIntrinsicsFeature( + Action action, + HwIntrinsics intrinsics, + T arg1, + string arg2) + where T : IXunitSerializable + { + if (!RemoteExecutor.IsSupported) + { + return; + } + + foreach (KeyValuePair intrinsic in intrinsics.ToFeatureKeyValueCollection()) + { + ProcessStartInfo processStartInfo = new(); + if (intrinsic.Key != HwIntrinsics.AllowAll) + { + processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; + + RemoteExecutor.Invoke( + action, + BasicSerializer.Serialize(arg1), + arg2, + new RemoteInvokeOptions + { + StartInfo = processStartInfo + }) + .Dispose(); + } + else + { + // Since we are running using the default architecture there is no + // point creating the overhead of running the action in a separate process. + action(BasicSerializer.Serialize(arg1), arg2); + } + } + } + /// /// Runs the given test within an environment /// where the given features. diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 3285da31b4..3652d77a1e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -207,7 +207,7 @@ public abstract partial class TestImageProvider : IXunitSerializable Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(options, nameof(options)); - options.Configuration = this.Configuration; + options.SetConfiguration(this.Configuration); // Used in small subset of decoder tests, no caching. // TODO: Check Path here. Why combined? @@ -244,7 +244,7 @@ public abstract partial class TestImageProvider : IXunitSerializable Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(options, nameof(options)); - options.GeneralOptions.Configuration = this.Configuration; + options.GeneralOptions.SetConfiguration(this.Configuration); // Used in small subset of decoder tests, no caching. // TODO: Check Path here. Why combined? @@ -268,7 +268,7 @@ public abstract partial class TestImageProvider : IXunitSerializable private Image DecodeImage(IImageDecoder decoder, DecoderOptions options) { - options.Configuration = this.Configuration; + options.SetConfiguration(this.Configuration); var testFile = TestFile.Create(this.FilePath); using Stream stream = new MemoryStream(testFile.Bytes); @@ -278,7 +278,7 @@ public abstract partial class TestImageProvider : IXunitSerializable private Image DecodeImage(ISpecializedImageDecoder decoder, T options) where T : class, ISpecializedDecoderOptions, new() { - options.GeneralOptions.Configuration = this.Configuration; + options.GeneralOptions.SetConfiguration(this.Configuration); var testFile = TestFile.Create(this.FilePath); using Stream stream = new MemoryStream(testFile.Bytes); diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index a0fdb13319..ae09c4f3f2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -75,7 +75,7 @@ public class MagickReferenceDecoder : ImageDecoder protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { using Image image = this.Decode(options, stream, cancellationToken); - return new(image.PixelType, image.Width, image.Height, image.Metadata); + return new(image.PixelType, image.Size, image.Metadata, new List(image.Frames.Select(x => x.Metadata))); } private static void FromRgba32Bytes(Configuration configuration, Span rgbaBytes, IMemoryGroup destinationGroup) diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 2deed6d488..a3408bedb4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -17,7 +17,7 @@ public class SystemDrawingReferenceDecoder : ImageDecoder { using SDBitmap sourceBitmap = new(stream); PixelTypeInfo pixelType = new(SDImage.GetPixelFormatSize(sourceBitmap.PixelFormat)); - return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata()); + return new ImageInfo(pixelType, new(sourceBitmap.Width, sourceBitmap.Height), new ImageMetadata()); } protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 0a3e45b3c8..cbce961103 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; @@ -366,7 +367,7 @@ public class TestImageProviderTests protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { using Image image = this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken); - return new(image.PixelType, image.Width, image.Height, image.Metadata); + return new(image.PixelType, image.Size, image.Metadata, new List(image.Frames.Select(x => x.Metadata))); } protected override Image Decode(TestDecoderOptions options, Stream stream, CancellationToken cancellationToken) @@ -409,7 +410,7 @@ public class TestImageProviderTests protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { using Image image = this.Decode(this.CreateDefaultSpecializedOptions(options), stream, cancellationToken); - return new(image.PixelType, image.Width, image.Height, image.Metadata); + return new(image.PixelType, image.Size, image.Metadata, new List(image.Frames.Select(x => x.Metadata))); } protected override Image Decode(TestDecoderWithParametersOptions options, Stream stream, CancellationToken cancellationToken) diff --git a/tests/ImageSharp.Tests/xunit.runner.json b/tests/ImageSharp.Tests/xunit.runner.json index 749ece4387..d7b466d09d 100644 --- a/tests/ImageSharp.Tests/xunit.runner.json +++ b/tests/ImageSharp.Tests/xunit.runner.json @@ -1,5 +1,6 @@ { "shadowCopy": false, "methodDisplay": "method", - "diagnosticMessages": true + "diagnosticMessages": true, + "preEnumerateTheories": false } diff --git a/tests/Images/External/ReferenceOutput/PorterDuffCompositorTests/PorterDuffOutputIsCorrect_Rgba32_pd-dest_DestOut.png b/tests/Images/External/ReferenceOutput/PorterDuffCompositorTests/PorterDuffOutputIsCorrect_Rgba32_pd-dest_DestOut.png index bd50175d7c..ebd022edbf 100644 --- a/tests/Images/External/ReferenceOutput/PorterDuffCompositorTests/PorterDuffOutputIsCorrect_Rgba32_pd-dest_DestOut.png +++ b/tests/Images/External/ReferenceOutput/PorterDuffCompositorTests/PorterDuffOutputIsCorrect_Rgba32_pd-dest_DestOut.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2952981bde801d566e5b7503722907b2f4318b71c327276735e929d9f598ffc0 -size 1707 +oid sha256:9562915ef66d9ca6a42108cae51ad08fa711dddc79aaedad2c4be83bd8c38534 +size 1952 diff --git a/tests/Images/External/ReferenceOutput/PorterDuffCompositorTests/PorterDuffOutputIsCorrect_Rgba32_pd-dest_In.png b/tests/Images/External/ReferenceOutput/PorterDuffCompositorTests/PorterDuffOutputIsCorrect_Rgba32_pd-dest_In.png index 265985c3f2..4dcb0c4294 100644 --- a/tests/Images/External/ReferenceOutput/PorterDuffCompositorTests/PorterDuffOutputIsCorrect_Rgba32_pd-dest_In.png +++ b/tests/Images/External/ReferenceOutput/PorterDuffCompositorTests/PorterDuffOutputIsCorrect_Rgba32_pd-dest_In.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de9b2fbf7dce7980ade102386aa070c48a31ee32807a13a28b4e172c0fc26a16 -size 1561 +oid sha256:dfb99359c216d5aee6eabed9f549c406e3b18f50fb255db2a936cba217888cd0 +size 1740 diff --git a/tests/Images/External/ReferenceOutput/PorterDuffCompositorTests/PorterDuffOutputIsCorrect_Rgba32_pd-dest_Out.png b/tests/Images/External/ReferenceOutput/PorterDuffCompositorTests/PorterDuffOutputIsCorrect_Rgba32_pd-dest_Out.png index 8cdaf949d2..44c0a812a3 100644 --- a/tests/Images/External/ReferenceOutput/PorterDuffCompositorTests/PorterDuffOutputIsCorrect_Rgba32_pd-dest_Out.png +++ b/tests/Images/External/ReferenceOutput/PorterDuffCompositorTests/PorterDuffOutputIsCorrect_Rgba32_pd-dest_Out.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3effcb28b17363a5ee26d38d55f7354e1e9499824863cb389a1b002c18ad3644 -size 2099 +oid sha256:d2049fbfdebb20807ea1d402e1a0b8c8bed0995d16900f5739ad0db5d2acd501 +size 2273 diff --git a/tests/Images/Input/Tiff/cmyk_deflate_64bit.tiff b/tests/Images/Input/Tiff/cmyk_deflate_64bit.tiff new file mode 100644 index 0000000000..f267c5e152 --- /dev/null +++ b/tests/Images/Input/Tiff/cmyk_deflate_64bit.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45793bfd1c9e92910b5b38805499859c38bb2fa1a2ae0c22888c16cc88b25d23 +size 53002