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 7c7504371d..379be36107 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -196,9 +196,9 @@ 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
+ dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.FEEDZ_TOKEN}} -s https://f.feedz.io/sixlabors/sixlabors/nuget/index.json
+ dotnet nuget push .\artifacts\*.snupkg -k ${{secrets.FEEDZ_TOKEN}} -s https://f.feedz.io/sixlabors/sixlabors/symbols
# TODO: If github.ref starts with 'refs/tags' then it was tag push and we can optionally push out package to nuget.org
diff --git a/README.md b/README.md
index 4d0eb1e3a6..4962faf5b3 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` | [](https://www.nuget.org/packages/SixLabors.ImageSharp/) | [](https://www.myget.org/feed/sixlabors/package/nuget/SixLabors.ImageSharp) |
+| `SixLabors.ImageSharp` | [](https://www.nuget.org/packages/SixLabors.ImageSharp/) | [](https://f.feedz.io/sixlabors/sixlabors/nuget/index.json) |
## Manual build
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/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/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/Shuffle/IComponentShuffle.cs b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs
index 45d6e6d13d..345f4b5c2e 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,37 +46,25 @@ 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)
+ public DefaultShuffle4(byte control)
{
- 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);
+ DebugGuard.MustBeBetweenOrEqualTo(control, 0, 3, nameof(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 int p3, out int p2, out int p1, out int p0);
for (int i = 0; i < source.Length; i += 4)
{
@@ -86,11 +78,9 @@ 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)
@@ -112,11 +102,9 @@ 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)
@@ -138,11 +126,9 @@ 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)
@@ -164,11 +150,9 @@ 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)
@@ -197,11 +181,9 @@ 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)
diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs
index 76cffd82bf..348e8ec010 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,37 +14,25 @@ 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)
+ public DefaultPad3Shuffle4(byte control)
{
- 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);
+ DebugGuard.MustBeBetweenOrEqualTo(control, 0, 3, nameof(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 int p3, out int p2, out int p1, out int p0);
Span temp = stackalloc byte[4];
ref byte t = ref MemoryMarshal.GetReference(temp);
@@ -51,7 +40,7 @@ internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4
for (int i = 0, j = 0; i < 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);
@@ -64,11 +53,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)
diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs
index 9bee9d15ec..6ac8a2428a 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,33 +14,25 @@ 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)
+ public DefaultShuffle3(byte control)
{
- 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);
+ DebugGuard.MustBeBetweenOrEqualTo(control, 0, 3, nameof(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 int p2, out int p1, out int p0);
for (int i = 0; i < source.Length; i += 3)
{
diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs
index 90b77b568e..d4a5e8130a 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,34 +14,25 @@ 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)
+ public DefaultShuffle4Slice3(byte control)
{
- 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);
+ DebugGuard.MustBeBetweenOrEqualTo(control, 0, 3, nameof(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 int p2, out int p1, out int p0);
for (int i = 0, j = 0; i < dest.Length; i += 3, j += 4)
{
@@ -53,11 +45,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)
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
index 4bc0040c67..3841b64b4d 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
@@ -297,7 +297,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 =
@@ -333,7 +333,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 =
@@ -382,7 +382,7 @@ internal static partial class SimdUtils
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 =
@@ -445,7 +445,7 @@ internal static partial class SimdUtils
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 =
@@ -489,7 +489,7 @@ internal static partial class SimdUtils
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 =
@@ -532,7 +532,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.
@@ -549,22 +550,21 @@ internal static partial class SimdUtils
{
return Fma.MultiplyAdd(vm1, vm0, va);
}
- else
- {
- return Avx.Add(Avx.Multiply(vm0, vm1), va);
- }
+
+ return Avx.Add(Avx.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(
+ public static Vector256 MultiplySubtract(
in Vector256 vs,
in Vector256 vm0,
in Vector256 vm1)
@@ -573,10 +573,30 @@ internal static partial class SimdUtils
{
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(
+ in Vector256 a,
+ in Vector256 b,
+ in 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));
}
///
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Shuffle.cs
index b91dc2fad2..aecdaa6390 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,7 +145,7 @@ 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 int p3, out int p2, out int p1, out int p0);
for (int i = 0; i < source.Length; i += 4)
{
@@ -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,14 +226,271 @@ 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,
@@ -248,7 +509,7 @@ internal static partial class SimdUtils
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void InverseMmShuffle(
+ public static void InverseMMShuffle(
byte control,
out int p3,
out int p2,
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/Webp/AlphaDecoder.cs b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs
index d45fa2a427..8875ae1150 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).
@@ -327,7 +324,7 @@ internal class AlphaDecoder : IDisposable
ref byte srcRef = ref MemoryMarshal.GetReference(input);
for (i = 1; i + 8 <= width; 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);
@@ -365,32 +362,29 @@ internal class AlphaDecoder : IDisposable
{
HorizontalUnfilter(null, input, dst, width);
}
- else
+ else if (Avx2.IsSupported)
{
- if (Avx2.IsSupported)
+ nint i;
+ int maxPos = width & ~31;
+ for (i = 0; i < 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 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;
+ }
- for (; i < width; i++)
- {
- dst[(int)i] = (byte)(prev[(int)i] + input[(int)i]);
- }
+ for (; i < width; i++)
+ {
+ dst[(int)i] = (byte)(prev[(int)i] + input[(int)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..8da717545f 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;
+ System.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..02b1d0ab6a 100644
--- a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
+++ b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
@@ -123,7 +123,7 @@ 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));
@@ -207,7 +207,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)
{
diff --git a/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs b/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
index 6fa02c1161..b83b44fa14 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)
@@ -465,7 +459,7 @@ internal class Vp8BitWriter : BitWriterBase
int mbSize = this.enc.Mbw * this.enc.Mbh;
int expectedSize = 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..22bc195d64 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;
@@ -138,25 +137,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)
diff --git a/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs
index b3589b52c7..df19c26e0b 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;
@@ -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);
@@ -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/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/LosslessUtils.cs b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs
index 5d9c207096..ac92ce6a6a 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs
@@ -96,7 +96,7 @@ internal static unsafe class LosslessUtils
{
if (Avx2.IsSupported)
{
- 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);
+ 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);
int numPixels = pixelData.Length;
nint i;
for (i = 0; i <= numPixels - 8; i += 8)
@@ -115,7 +115,7 @@ internal static unsafe class LosslessUtils
}
else if (Ssse3.IsSupported)
{
- var addGreenToBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255);
+ Vector128 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)
@@ -138,13 +138,11 @@ internal static unsafe class LosslessUtils
nint i;
for (i = 0; i <= numPixels - 4; i += 4)
{
- 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();
}
@@ -178,7 +176,7 @@ internal static unsafe class LosslessUtils
{
if (Avx2.IsSupported)
{
- 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);
+ 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);
int numPixels = pixelData.Length;
nint i;
for (i = 0; i <= numPixels - 8; i += 8)
@@ -197,7 +195,7 @@ internal static unsafe class LosslessUtils
}
else if (Ssse3.IsSupported)
{
- var subtractGreenFromBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255);
+ Vector128 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)
@@ -220,13 +218,11 @@ internal static unsafe class LosslessUtils
nint i;
for (i = 0; i <= numPixels - 4; i += 4)
{
- 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();
}
@@ -334,7 +330,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 +367,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)
{
- 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());
@@ -403,20 +397,18 @@ internal static unsafe class LosslessUtils
}
else if (Sse2.IsSupported)
{
- 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)
{
- 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());
@@ -465,19 +457,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)
{
- 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);
@@ -496,20 +486,18 @@ internal static unsafe class LosslessUtils
}
else if (Sse2.IsSupported)
{
- 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)
{
- 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);
diff --git a/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs b/src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs
index abfb67bc7e..84ddd4b785 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
{
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
index a5d1900b2a..16b4c827ef 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
@@ -348,12 +348,18 @@ internal class Vp8Encoder : IDisposable
// 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);
+ using IMemoryOwner encodedAlphaData = AlphaEncoder.EncodeAlpha(
+ image,
+ this.configuration,
+ this.memoryAllocator,
+ this.skipMetadata,
+ this.alphaCompression,
+ out alphaDataSize);
+
alphaData = encodedAlphaData.GetSpan();
if (alphaDataSize < pixelCount)
{
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/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
index 4c1c60591d..abaa85ef18 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;
@@ -46,17 +45,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,8 +82,8 @@ 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();
@@ -99,12 +98,12 @@ internal class WebpAnimationDecoder : IDisposable
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, this.buffer);
break;
default:
WebpThrowHelper.ThrowImageFormatException("Read unexpected webp chunk data");
@@ -117,7 +116,7 @@ internal class WebpAnimationDecoder : IDisposable
}
}
- return image;
+ return image!;
}
///
@@ -130,7 +129,7 @@ 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);
@@ -146,7 +145,7 @@ internal class WebpAnimationDecoder : IDisposable
chunkType = WebpChunkParsingUtils.ReadChunkType(stream, this.buffer);
}
- WebpImageInfo webpInfo = null;
+ WebpImageInfo? webpInfo = null;
WebpFeatures features = new();
switch (chunkType)
{
@@ -163,7 +162,7 @@ internal class WebpAnimationDecoder : IDisposable
break;
}
- ImageFrame currentFrame = null;
+ ImageFrame? currentFrame = null;
ImageFrame imageFrame;
if (previousFrame is null)
{
@@ -175,7 +174,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);
diff --git a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs
index bfe415a6e4..e4acdf311c 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;
diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
index c4e2e0d55a..c158df44d9 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;
@@ -41,35 +38,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 +70,24 @@ 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();
- uint fileSize = this.ReadImageHeader();
+ uint fileSize = this.ReadImageHeader(stream);
- 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 +96,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);
}
return image;
@@ -147,34 +128,35 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
- this.currentStream = stream;
+ this.ReadImageHeader(stream);
- 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),
new((int)this.webImageInfo.Width, (int)this.webImageInfo.Height),
- this.metadata);
+ metadata);
}
}
///
/// Reads and skips over the image header.
///
+ /// The stream to decode from.
/// The file size in bytes.
- private uint ReadImageHeader()
+ private uint ReadImageHeader(BufferedReadStream stream)
{
// 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, this.buffer);
// Skip 'WEBP' from the header.
- this.currentStream.Skip(4);
+ stream.Skip(4);
return fileSize;
}
@@ -182,42 +164,43 @@ 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);
+ WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, this.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, this.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, this.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, this.buffer, features);
+ while (stream.Position < stream.Length)
{
- chunkType = WebpChunkParsingUtils.ReadChunkType(this.currentStream, this.buffer);
+ chunkType = WebpChunkParsingUtils.ReadChunkType(stream, this.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, this.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, this.buffer, features);
}
else if (WebpChunkParsingUtils.IsOptionalVp8XChunk(chunkType))
{
- bool isAnimationChunk = this.ParseOptionalExtendedChunks(chunkType, features, ignoreAlpha);
+ bool isAnimationChunk = this.ParseOptionalExtendedChunks(stream, metadata, chunkType, features, ignoreAlpha);
if (isAnimationChunk)
{
return webpInfos;
@@ -226,8 +209,8 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
else
{
// Ignore unknown chunks.
- uint chunkSize = this.ReadChunkSize();
- this.currentStream.Skip((int)chunkSize);
+ uint chunkSize = this.ReadChunkSize(stream);
+ stream.Skip((int)chunkSize);
}
}
@@ -242,32 +225,39 @@ 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.
/// 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)
{
switch (chunkType)
{
case WebpChunkType.Iccp:
- this.ReadIccProfile();
+ this.ReadIccProfile(stream, metadata);
break;
case WebpChunkType.Exif:
- this.ReadExifProfile();
+ this.ReadExifProfile(stream, metadata);
break;
case WebpChunkType.Xmp:
- this.ReadXmpProfile();
+ this.ReadXmpProfile(stream, metadata);
break;
case WebpChunkType.AnimationParameter:
- this.ReadAnimationParameters(features);
+ this.ReadAnimationParameters(stream, features);
return true;
case WebpChunkType.Alpha:
- this.ReadAlphaData(features, ignoreAlpha);
+ this.ReadAlphaData(stream, features, ignoreAlpha);
break;
default:
WebpThrowHelper.ThrowImageFormatException("Unexpected chunk followed VP8X header");
@@ -280,32 +270,34 @@ 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)
+ private void ParseOptionalChunks(BufferedReadStream stream, ImageMetadata metadata, WebpFeatures features)
{
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 = this.ReadChunkType(stream);
+ if (chunkType == WebpChunkType.Exif && metadata.ExifProfile == null)
{
- this.ReadExifProfile();
+ this.ReadExifProfile(stream, metadata);
}
- else if (chunkType == WebpChunkType.Xmp && this.metadata.XmpProfile == null)
+ else if (chunkType == WebpChunkType.Xmp && metadata.XmpProfile == null)
{
- this.ReadXmpProfile();
+ this.ReadXmpProfile(stream, metadata);
}
else
{
// Skip duplicate XMP or EXIF chunk.
- uint chunkLength = this.ReadChunkSize();
- this.currentStream.Skip((int)chunkLength);
+ uint chunkLength = this.ReadChunkSize(stream);
+ stream.Skip((int)chunkLength);
}
}
}
@@ -313,76 +305,80 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
/// Reads the EXIF profile from the stream.
///
- private void ReadExifProfile()
+ /// The stream to decode from.
+ /// The image metadata.
+ private void ReadExifProfile(BufferedReadStream stream, ImageMetadata metadata)
{
- uint exifChunkSize = this.ReadChunkSize();
+ uint exifChunkSize = this.ReadChunkSize(stream);
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.
+ private void ReadXmpProfile(BufferedReadStream stream, ImageMetadata metadata)
{
- uint xmpChunkSize = this.ReadChunkSize();
+ uint xmpChunkSize = this.ReadChunkSize(stream);
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.
+ private void ReadIccProfile(BufferedReadStream stream, ImageMetadata metadata)
{
- uint iccpChunkSize = this.ReadChunkSize();
+ uint iccpChunkSize = this.ReadChunkSize(stream);
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;
}
}
}
@@ -390,17 +386,18 @@ 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)
+ private void ReadAnimationParameters(BufferedReadStream stream, WebpFeatures features)
{
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, this.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(this.buffer, 0, 2);
if (bytesRead != 2)
{
WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the animation loop count");
@@ -412,22 +409,23 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
/// 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)
+ private void ReadAlphaData(BufferedReadStream stream, WebpFeatures features, bool ignoreAlpha)
{
- uint alphaChunkSize = WebpChunkParsingUtils.ReadChunkSize(this.currentStream, this.buffer);
+ uint alphaChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, this.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");
@@ -437,15 +435,15 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
///
/// Identifies the chunk type from the chunk.
///
+ /// The stream to decode from.
///
/// Thrown if the input stream is not valid.
///
- private WebpChunkType ReadChunkType()
+ private WebpChunkType ReadChunkType(BufferedReadStream stream)
{
- if (this.currentStream.Read(this.buffer, 0, 4) == 4)
+ if (stream.Read(this.buffer, 0, 4) == 4)
{
- var chunkType = (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer);
- return chunkType;
+ return (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer);
}
throw new ImageFormatException("Invalid Webp data.");
@@ -455,10 +453,12 @@ 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.
/// The chunk size in bytes.
- private uint ReadChunkSize()
+ /// Invalid data.
+ private uint ReadChunkSize(BufferedReadStream stream)
{
- if (this.currentStream.Read(this.buffer, 0, 4) == 4)
+ if (stream.Read(this.buffer, 0, 4) == 4)
{
uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(this.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..33189ba845 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;
@@ -81,16 +79,17 @@ 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;
@@ -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/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/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/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/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/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
index cf19101211..2db61a06f3 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, (IntPtr)((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, (IntPtr)((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, (IntPtr)((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, (IntPtr)((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, (IntPtr)((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, (IntPtr)((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, (IntPtr)((uint)destination.Length / 2u));
+
+ ref Vector256 backgroundBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(background));
+ ref Vector256 sourceBase = ref Unsafe.As