diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.ByteOutputOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.ByteOutputOperator.cs
new file mode 100644
index 000000000..3066c6841
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.ByteOutputOperator.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef;
+
+internal static partial class Av1CdefFilter
+{
+ ///
+ /// Writes filtered samples to eight-bit plane storage.
+ ///
+ private readonly struct ByteOutputOperator : IOutputOperator
+ {
+ ///
+ public static void StoreVector(ref byte destination, int offset, Vector128 value, int count)
+ {
+ Vector64 packed = Vector128.Narrow(value.AsUInt16(), Vector128.Zero).GetLower();
+ ref byte output = ref Unsafe.Add(ref destination, offset);
+ if (count == 8)
+ {
+ packed.StoreUnsafe(ref output);
+ }
+ else
+ {
+ Unsafe.WriteUnaligned(ref output, packed.AsUInt32().ToScalar());
+ }
+ }
+
+ ///
+ public static void StoreScalar(ref byte destination, int offset, int value) => Unsafe.Add(ref destination, offset) = (byte)value;
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.CopyFilterOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.CopyFilterOperator.cs
new file mode 100644
index 000000000..8a28ad418
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.CopyFilterOperator.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef;
+
+internal static partial class Av1CdefFilter
+{
+ ///
+ /// Disables both tap groups so the source block is copied unchanged.
+ ///
+ private readonly struct CopyFilterOperator : IFilterOperator
+ {
+ ///
+ public static bool EnablePrimary => false;
+
+ ///
+ public static bool EnableSecondary => false;
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.Operator.cs
new file mode 100644
index 000000000..df9e5b060
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.Operator.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Runtime.Intrinsics;
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef;
+
+internal static partial class Av1CdefFilter
+{
+ ///
+ /// Defines storage-specific writes for one filtered row.
+ ///
+ /// The destination sample storage type.
+ private interface IOutputOperator
+ where TSample : unmanaged
+ {
+ ///
+ /// Stores four or eight filtered samples from the low vector lanes.
+ ///
+ /// The first element in the destination plane.
+ /// The offset of the first sample to write.
+ /// The filtered samples in the low lanes.
+ /// The number of valid lanes.
+ public static abstract void StoreVector(ref TSample destination, int offset, Vector128 value, int count);
+
+ ///
+ /// Stores one filtered sample.
+ ///
+ /// The first element in the destination plane.
+ /// The offset of the sample to write.
+ /// The filtered sample.
+ public static abstract void StoreScalar(ref TSample destination, int offset, int value);
+ }
+
+ ///
+ /// Defines which groups of directional taps participate in one closed filter kernel.
+ ///
+ private interface IFilterOperator
+ {
+ ///
+ /// Gets a value indicating whether the primary directional taps are enabled.
+ ///
+ public static abstract bool EnablePrimary { get; }
+
+ ///
+ /// Gets a value indicating whether the secondary off-axis taps are enabled.
+ ///
+ public static abstract bool EnableSecondary { get; }
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.PrimaryAndSecondaryFilterOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.PrimaryAndSecondaryFilterOperator.cs
new file mode 100644
index 000000000..2bfc98a7d
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.PrimaryAndSecondaryFilterOperator.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef;
+
+internal static partial class Av1CdefFilter
+{
+ ///
+ /// Enables both directional tap groups and their combined clipping rule.
+ ///
+ private readonly struct PrimaryAndSecondaryFilterOperator : IFilterOperator
+ {
+ ///
+ public static bool EnablePrimary => true;
+
+ ///
+ public static bool EnableSecondary => true;
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.PrimaryFilterOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.PrimaryFilterOperator.cs
new file mode 100644
index 000000000..091835f8e
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.PrimaryFilterOperator.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef;
+
+internal static partial class Av1CdefFilter
+{
+ ///
+ /// Enables only the primary directional taps.
+ ///
+ private readonly struct PrimaryFilterOperator : IFilterOperator
+ {
+ ///
+ public static bool EnablePrimary => true;
+
+ ///
+ public static bool EnableSecondary => false;
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.SecondaryFilterOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.SecondaryFilterOperator.cs
new file mode 100644
index 000000000..0a1521c02
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.SecondaryFilterOperator.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef;
+
+internal static partial class Av1CdefFilter
+{
+ ///
+ /// Enables only the secondary off-axis taps.
+ ///
+ private readonly struct SecondaryFilterOperator : IFilterOperator
+ {
+ ///
+ public static bool EnablePrimary => false;
+
+ ///
+ public static bool EnableSecondary => true;
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.UInt16OutputOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.UInt16OutputOperator.cs
new file mode 100644
index 000000000..df80b2031
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.UInt16OutputOperator.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef;
+
+internal static partial class Av1CdefFilter
+{
+ ///
+ /// Writes filtered samples to 16-bit plane storage.
+ ///
+ private readonly struct UInt16OutputOperator : IOutputOperator
+ {
+ ///
+ public static void StoreVector(ref ushort destination, int offset, Vector128 value, int count)
+ {
+ ref ushort output = ref Unsafe.Add(ref destination, offset);
+ if (count == 8)
+ {
+ value.AsUInt16().StoreUnsafe(ref output);
+ }
+ else
+ {
+ ref byte outputBytes = ref Unsafe.As(ref output);
+ Unsafe.WriteUnaligned(ref outputBytes, value.AsUInt64().GetLower().ToScalar());
+ }
+ }
+
+ ///
+ public static void StoreScalar(ref ushort destination, int offset, int value) => Unsafe.Add(ref destination, offset) = (ushort)value;
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.cs
index 5e04a01f6..fe39e14c0 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.cs
@@ -18,54 +18,13 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef;
/// one eight-by-eight block per 128-bit lane so AVX2 can evaluate two independent blocks together. Scalar kernels retain
/// the same constrain, clipping, and tie-breaking rules for unsupported hardware and partial edge blocks.
///
-internal static class Av1CdefFilter
+internal static partial class Av1CdefFilter
{
///
/// The sample value used in the bordered source plane for neighbors outside the coded frame.
///
public const ushort VeryLarge = 0x4000;
- ///
- /// Defines storage-specific writes for one filtered row.
- ///
- /// The destination sample storage type.
- private interface IOutputOperator
- where TSample : unmanaged
- {
- ///
- /// Stores four or eight filtered samples from the low vector lanes.
- ///
- /// The first element in the destination plane.
- /// The offset of the first sample to write.
- /// The filtered samples in the low lanes.
- /// The number of valid lanes.
- public static abstract void StoreVector(ref TSample destination, int offset, Vector128 value, int count);
-
- ///
- /// Stores one filtered sample.
- ///
- /// The first element in the destination plane.
- /// The offset of the sample to write.
- /// The filtered sample.
- public static abstract void StoreScalar(ref TSample destination, int offset, int value);
- }
-
- ///
- /// Defines which groups of directional taps participate in one closed filter kernel.
- ///
- private interface IFilterOperator
- {
- ///
- /// Gets a value indicating whether the primary directional taps are enabled.
- ///
- public static abstract bool EnablePrimary { get; }
-
- ///
- /// Gets a value indicating whether the secondary off-axis taps are enabled.
- ///
- public static abstract bool EnableSecondary { get; }
- }
-
///
/// Copies an eight-bit sample rectangle into the 16-bit CDEF working plane.
///
@@ -1732,100 +1691,4 @@ internal static class Av1CdefFilter
6 => tap == 0 ? stride : 2 * stride,
_ => tap == 0 ? stride : (2 * stride) - 1
};
-
- ///
- /// Enables both directional tap groups and their combined clipping rule.
- ///
- private readonly struct PrimaryAndSecondaryFilterOperator : IFilterOperator
- {
- ///
- public static bool EnablePrimary => true;
-
- ///
- public static bool EnableSecondary => true;
- }
-
- ///
- /// Enables only the primary directional taps.
- ///
- private readonly struct PrimaryFilterOperator : IFilterOperator
- {
- ///
- public static bool EnablePrimary => true;
-
- ///
- public static bool EnableSecondary => false;
- }
-
- ///
- /// Enables only the secondary off-axis taps.
- ///
- private readonly struct SecondaryFilterOperator : IFilterOperator
- {
- ///
- public static bool EnablePrimary => false;
-
- ///
- public static bool EnableSecondary => true;
- }
-
- ///
- /// Disables both tap groups so the source block is copied unchanged.
- ///
- private readonly struct CopyFilterOperator : IFilterOperator
- {
- ///
- public static bool EnablePrimary => false;
-
- ///
- public static bool EnableSecondary => false;
- }
-
- ///
- /// Writes filtered samples to eight-bit plane storage.
- ///
- private readonly struct ByteOutputOperator : IOutputOperator
- {
- ///
- public static void StoreVector(ref byte destination, int offset, Vector128 value, int count)
- {
- Vector64 packed = Vector128.Narrow(value.AsUInt16(), Vector128.Zero).GetLower();
- ref byte output = ref Unsafe.Add(ref destination, offset);
- if (count == 8)
- {
- packed.StoreUnsafe(ref output);
- }
- else
- {
- Unsafe.WriteUnaligned(ref output, packed.AsUInt32().ToScalar());
- }
- }
-
- ///
- public static void StoreScalar(ref byte destination, int offset, int value) => Unsafe.Add(ref destination, offset) = (byte)value;
- }
-
- ///
- /// Writes filtered samples to 16-bit plane storage.
- ///
- private readonly struct UInt16OutputOperator : IOutputOperator
- {
- ///
- public static void StoreVector(ref ushort destination, int offset, Vector128 value, int count)
- {
- ref ushort output = ref Unsafe.Add(ref destination, offset);
- if (count == 8)
- {
- value.AsUInt16().StoreUnsafe(ref output);
- }
- else
- {
- ref byte outputBytes = ref Unsafe.As(ref output);
- Unsafe.WriteUnaligned(ref outputBytes, value.AsUInt64().GetLower().ToScalar());
- }
- }
-
- ///
- public static void StoreScalar(ref ushort destination, int offset, int value) => Unsafe.Add(ref destination, offset) = (ushort)value;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainNoise.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainNoise.cs
index 48224ef0c..847204f1b 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainNoise.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainNoise.cs
@@ -271,10 +271,10 @@ internal static class Av1FilmGrainNoise
for (; column <= vectorEnd; column += Vector256.Count)
{
ref TSample destination = ref Unsafe.Add(ref sampleBase, sampleRowOffset + column);
- Vector256 source = Av1FilmGrainSampleOperator.Load8(ref destination);
+ Vector256 source = Av1FilmGrainSampleOperations.Load8(ref destination);
Vector256 grainValues = Vector256.LoadUnsafe(ref grainBase, (nuint)(grainRowOffset + column));
Vector256 result = AddNoise(source, grainValues, scaling, bitDepth, roundingOffset, scalingShift, minimum, maximum);
- Av1FilmGrainSampleOperator.Store8(ref destination, result);
+ Av1FilmGrainSampleOperations.Store8(ref destination, result);
}
ApplyLumaScalar(
@@ -326,10 +326,10 @@ internal static class Av1FilmGrainNoise
for (; column <= vectorEnd; column += Vector128.Count)
{
ref TSample destination = ref Unsafe.Add(ref sampleBase, sampleRowOffset + column);
- Vector128 source = Av1FilmGrainSampleOperator.Load4(ref destination);
+ Vector128 source = Av1FilmGrainSampleOperations.Load4(ref destination);
Vector128 grainValues = Vector128.LoadUnsafe(ref grainBase, (nuint)(grainRowOffset + column));
Vector128 result = AddNoise(source, grainValues, scaling, bitDepth, roundingOffset, scalingShift, minimum, maximum);
- Av1FilmGrainSampleOperator.Store4(ref destination, result);
+ Av1FilmGrainSampleOperations.Store4(ref destination, result);
}
ApplyLumaScalar(
@@ -375,10 +375,10 @@ internal static class Av1FilmGrainNoise
for (int column = 0; column < width; column++)
{
ref TSample destination = ref Unsafe.Add(ref sampleBase, sampleRowOffset + column);
- int source = Av1FilmGrainSampleOperator.Load(ref destination);
+ int source = Av1FilmGrainSampleOperations.Load(ref destination);
int scale = ScaleLookup(scaling, source, bitDepth);
int value = source + (((scale * Unsafe.Add(ref grainBase, grainRowOffset + column)) + roundingOffset) >> scalingShift);
- Av1FilmGrainSampleOperator.Store(ref destination, Av1Math.Clamp(value, minimum, maximum));
+ Av1FilmGrainSampleOperations.Store(ref destination, Av1Math.Clamp(value, minimum, maximum));
}
}
}
@@ -577,11 +577,11 @@ internal static class Av1FilmGrainNoise
for (; column <= vectorEnd; column += Vector256.Count)
{
ref TSample lumaSource = ref Unsafe.Add(ref lumaRow, column << subsamplingX);
- Vector256 averageLuma = Av1FilmGrainSampleOperator.LoadChromaLuma8(ref lumaSource, subsamplingX);
+ Vector256 averageLuma = Av1FilmGrainSampleOperations.LoadChromaLuma8(ref lumaSource, subsamplingX);
if (applyCb)
{
ref TSample destination = ref Unsafe.Add(ref cbBase, chromaRowOffset + column);
- Vector256 source = Av1FilmGrainSampleOperator.Load8(ref destination);
+ Vector256 source = Av1FilmGrainSampleOperations.Load8(ref destination);
Vector256 scalingIndex = ((averageLuma * cbLumaMultiplier) + (source * cbMultiplier)) >> 6;
scalingIndex = Vector256.Min(Vector256.Max(scalingIndex + Vector256.Create(cbOffset), zero), maximumIndex);
Vector256 grainValues = Vector256.LoadUnsafe(ref cbGrainBase, (nuint)(grainRowOffset + column));
@@ -596,13 +596,13 @@ internal static class Av1FilmGrainNoise
minimum,
maximum);
- Av1FilmGrainSampleOperator.Store8(ref destination, result);
+ Av1FilmGrainSampleOperations.Store8(ref destination, result);
}
if (applyCr)
{
ref TSample destination = ref Unsafe.Add(ref crBase, chromaRowOffset + column);
- Vector256 source = Av1FilmGrainSampleOperator.Load8(ref destination);
+ Vector256 source = Av1FilmGrainSampleOperations.Load8(ref destination);
Vector256 scalingIndex = ((averageLuma * crLumaMultiplier) + (source * crMultiplier)) >> 6;
scalingIndex = Vector256.Min(Vector256.Max(scalingIndex + Vector256.Create(crOffset), zero), maximumIndex);
Vector256 grainValues = Vector256.LoadUnsafe(ref crGrainBase, (nuint)(grainRowOffset + column));
@@ -617,7 +617,7 @@ internal static class Av1FilmGrainNoise
minimum,
maximum);
- Av1FilmGrainSampleOperator.Store8(ref destination, result);
+ Av1FilmGrainSampleOperations.Store8(ref destination, result);
}
}
@@ -708,11 +708,11 @@ internal static class Av1FilmGrainNoise
for (; column <= vectorEnd; column += Vector128.Count)
{
ref TSample lumaSource = ref Unsafe.Add(ref lumaRow, column << subsamplingX);
- Vector128 averageLuma = Av1FilmGrainSampleOperator.LoadChromaLuma4(ref lumaSource, subsamplingX);
+ Vector128 averageLuma = Av1FilmGrainSampleOperations.LoadChromaLuma4(ref lumaSource, subsamplingX);
if (applyCb)
{
ref TSample destination = ref Unsafe.Add(ref cbBase, chromaRowOffset + column);
- Vector128 source = Av1FilmGrainSampleOperator.Load4(ref destination);
+ Vector128 source = Av1FilmGrainSampleOperations.Load4(ref destination);
Vector128 scalingIndex = ((averageLuma * cbLumaMultiplier) + (source * cbMultiplier)) >> 6;
scalingIndex = Vector128.Min(Vector128.Max(scalingIndex + Vector128.Create(cbOffset), zero), maximumIndex);
Vector128 grainValues = Vector128.LoadUnsafe(ref cbGrainBase, (nuint)(grainRowOffset + column));
@@ -727,13 +727,13 @@ internal static class Av1FilmGrainNoise
minimum,
maximum);
- Av1FilmGrainSampleOperator.Store4(ref destination, result);
+ Av1FilmGrainSampleOperations.Store4(ref destination, result);
}
if (applyCr)
{
ref TSample destination = ref Unsafe.Add(ref crBase, chromaRowOffset + column);
- Vector128 source = Av1FilmGrainSampleOperator.Load4(ref destination);
+ Vector128 source = Av1FilmGrainSampleOperations.Load4(ref destination);
Vector128 scalingIndex = ((averageLuma * crLumaMultiplier) + (source * crMultiplier)) >> 6;
scalingIndex = Vector128.Min(Vector128.Max(scalingIndex + Vector128.Create(crOffset), zero), maximumIndex);
Vector128 grainValues = Vector128.LoadUnsafe(ref crGrainBase, (nuint)(grainRowOffset + column));
@@ -748,7 +748,7 @@ internal static class Av1FilmGrainNoise
minimum,
maximum);
- Av1FilmGrainSampleOperator.Store4(ref destination, result);
+ Av1FilmGrainSampleOperations.Store4(ref destination, result);
}
}
@@ -831,10 +831,10 @@ internal static class Av1FilmGrainNoise
for (int column = 0; column < width; column++)
{
int lumaOffset = lumaRowOffset + (column << subsamplingX);
- int averageLuma = Av1FilmGrainSampleOperator.Load(ref Unsafe.Add(ref lumaBase, lumaOffset));
+ int averageLuma = Av1FilmGrainSampleOperations.Load(ref Unsafe.Add(ref lumaBase, lumaOffset));
if (subsamplingX != 0)
{
- averageLuma = (averageLuma + Av1FilmGrainSampleOperator.Load(ref Unsafe.Add(ref lumaBase, lumaOffset + 1)) + 1) >> 1;
+ averageLuma = (averageLuma + Av1FilmGrainSampleOperations.Load(ref Unsafe.Add(ref lumaBase, lumaOffset + 1)) + 1) >> 1;
}
int chromaOffset = chromaRowOffset + column;
@@ -842,7 +842,7 @@ internal static class Av1FilmGrainNoise
if (applyCb)
{
ref TSample destination = ref Unsafe.Add(ref cbBase, chromaOffset);
- int source = Av1FilmGrainSampleOperator.Load(ref destination);
+ int source = Av1FilmGrainSampleOperations.Load(ref destination);
int scalingIndex = Av1Math.Clamp(
(((averageLuma * cbLumaMultiplier) + (source * cbMultiplier)) >> 6) + cbOffset,
0,
@@ -850,13 +850,13 @@ internal static class Av1FilmGrainNoise
int scale = ScaleLookup(scalingCb, scalingIndex, bitDepth);
int value = source + (((scale * Unsafe.Add(ref cbGrainBase, grainOffset)) + roundingOffset) >> scalingShift);
- Av1FilmGrainSampleOperator.Store(ref destination, Av1Math.Clamp(value, minimum, maximum));
+ Av1FilmGrainSampleOperations.Store(ref destination, Av1Math.Clamp(value, minimum, maximum));
}
if (applyCr)
{
ref TSample destination = ref Unsafe.Add(ref crBase, chromaOffset);
- int source = Av1FilmGrainSampleOperator.Load(ref destination);
+ int source = Av1FilmGrainSampleOperations.Load(ref destination);
int scalingIndex = Av1Math.Clamp(
(((averageLuma * crLumaMultiplier) + (source * crMultiplier)) >> 6) + crOffset,
0,
@@ -864,7 +864,7 @@ internal static class Av1FilmGrainNoise
int scale = ScaleLookup(scalingCr, scalingIndex, bitDepth);
int value = source + (((scale * Unsafe.Add(ref crGrainBase, grainOffset)) + roundingOffset) >> scalingShift);
- Av1FilmGrainSampleOperator.Store(ref destination, Av1Math.Clamp(value, minimum, maximum));
+ Av1FilmGrainSampleOperations.Store(ref destination, Av1Math.Clamp(value, minimum, maximum));
}
}
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainSampleOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainSampleOperations.cs
similarity index 99%
rename from src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainSampleOperator.cs
rename to src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainSampleOperations.cs
index ad648a7c3..eb855a710 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainSampleOperator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainSampleOperations.cs
@@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.FilmGrain;
/// row loops. All arithmetic uses signed 32-bit lanes; decoded samples are nonnegative and at most twelve bits, making
/// the intermediate signed 16-bit views safe wherever pairwise operations require them.
///
-internal readonly struct Av1FilmGrainSampleOperator
+internal readonly struct Av1FilmGrainSampleOperations
where TSample : unmanaged
{
///
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.HorizontalByteEdgeOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.HorizontalByteEdgeOperator.cs
new file mode 100644
index 000000000..8871d36a4
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.HorizontalByteEdgeOperator.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopFilter;
+
+internal static partial class Av1DeblockingFilter
+{
+ ///
+ /// Accesses four columns across a horizontal edge in eight-bit storage.
+ ///
+ private readonly struct HorizontalByteEdgeOperator : IEdgeOperator
+ {
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector128 LoadVector(ref byte samples, int q0Offset, int stride, int distance)
+ {
+ ref byte source = ref Unsafe.Add(ref samples, q0Offset + (distance * stride));
+ uint packed = Unsafe.ReadUnaligned(ref source);
+ Vector128 widened = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte());
+ return Vector128.WidenLower(widened).AsInt32();
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void StoreVector(ref byte samples, int q0Offset, int stride, int distance, Vector128 value)
+ {
+ Vector128 narrowed16 = Vector128.Narrow(value.AsUInt32(), Vector128.Zero);
+ Vector128 narrowed8 = Vector128.Narrow(narrowed16, Vector128.Zero);
+ Unsafe.WriteUnaligned(ref Unsafe.Add(ref samples, q0Offset + (distance * stride)), narrowed8.AsUInt32().ToScalar());
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LoadScalar(ref byte samples, int q0Offset, int stride, int distance, int index)
+ => Unsafe.Add(ref samples, q0Offset + (distance * stride) + index);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void StoreScalar(ref byte samples, int q0Offset, int stride, int distance, int index, int value)
+ => Unsafe.Add(ref samples, q0Offset + (distance * stride) + index) = (byte)value;
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.HorizontalUInt16EdgeOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.HorizontalUInt16EdgeOperator.cs
new file mode 100644
index 000000000..115b3bae5
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.HorizontalUInt16EdgeOperator.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopFilter;
+
+internal static partial class Av1DeblockingFilter
+{
+ ///
+ /// Accesses four columns across a horizontal edge in 16-bit storage.
+ ///
+ private readonly struct HorizontalUInt16EdgeOperator : IEdgeOperator
+ {
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector128 LoadVector(ref ushort samples, int q0Offset, int stride, int distance)
+ {
+ ref ushort source = ref Unsafe.Add(ref samples, q0Offset + (distance * stride));
+ ulong packed = Unsafe.ReadUnaligned(ref Unsafe.As(ref source));
+ return Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsUInt16()).AsInt32();
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void StoreVector(ref ushort samples, int q0Offset, int stride, int distance, Vector128 value)
+ {
+ Vector64 narrowed = Vector128.Narrow(value, Vector128.Zero).AsUInt16().GetLower();
+ ref byte destination = ref Unsafe.As(ref Unsafe.Add(ref samples, q0Offset + (distance * stride)));
+ Unsafe.WriteUnaligned(ref destination, narrowed.AsUInt64().ToScalar());
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LoadScalar(ref ushort samples, int q0Offset, int stride, int distance, int index)
+ => Unsafe.Add(ref samples, q0Offset + (distance * stride) + index);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void StoreScalar(ref ushort samples, int q0Offset, int stride, int distance, int index, int value)
+ => Unsafe.Add(ref samples, q0Offset + (distance * stride) + index) = (ushort)value;
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.Operator.cs
new file mode 100644
index 000000000..18f949b04
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.Operator.cs
@@ -0,0 +1,59 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Runtime.Intrinsics;
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopFilter;
+
+internal static partial class Av1DeblockingFilter
+{
+ ///
+ /// Defines orientation- and storage-specific access to the four samples running along one edge segment.
+ ///
+ /// The reconstructed sample storage type.
+ private interface IEdgeOperator
+ where TSample : unmanaged
+ {
+ ///
+ /// Loads four samples at one signed distance across the edge.
+ ///
+ /// The first element in the plane storage.
+ /// The offset of the first Q-side sample.
+ /// The number of samples between adjacent rows.
+ /// The signed sample distance from Q0.
+ /// The widened samples ordered along the edge.
+ public static abstract Vector128 LoadVector(ref TSample samples, int q0Offset, int stride, int distance);
+
+ ///
+ /// Stores four samples at one signed distance across the edge.
+ ///
+ /// The first element in the plane storage.
+ /// The offset of the first Q-side sample.
+ /// The number of samples between adjacent rows.
+ /// The signed sample distance from Q0.
+ /// The widened samples ordered along the edge.
+ public static abstract void StoreVector(ref TSample samples, int q0Offset, int stride, int distance, Vector128 value);
+
+ ///
+ /// Loads one sample at a signed distance across and an offset along the edge.
+ ///
+ /// The first element in the plane storage.
+ /// The offset of the first Q-side sample.
+ /// The number of samples between adjacent rows.
+ /// The signed sample distance from Q0.
+ /// The sample offset along the edge.
+ /// The selected sample.
+ public static abstract int LoadScalar(ref TSample samples, int q0Offset, int stride, int distance, int index);
+
+ ///
+ /// Stores one sample at a signed distance across and an offset along the edge.
+ ///
+ /// The first element in the plane storage.
+ /// The offset of the first Q-side sample.
+ /// The number of samples between adjacent rows.
+ /// The signed sample distance from Q0.
+ /// The sample offset along the edge.
+ /// The filtered sample.
+ public static abstract void StoreScalar(ref TSample samples, int q0Offset, int stride, int distance, int index, int value);
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.VerticalByteEdgeOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.VerticalByteEdgeOperator.cs
new file mode 100644
index 000000000..cd491a755
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.VerticalByteEdgeOperator.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopFilter;
+
+internal static partial class Av1DeblockingFilter
+{
+ ///
+ /// Accesses four rows across a vertical edge in eight-bit storage.
+ ///
+ private readonly struct VerticalByteEdgeOperator : IEdgeOperator
+ {
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector128 LoadVector(ref byte samples, int q0Offset, int stride, int distance)
+ => Vector128.Create(
+ (int)Unsafe.Add(ref samples, q0Offset + distance),
+ Unsafe.Add(ref samples, q0Offset + stride + distance),
+ Unsafe.Add(ref samples, q0Offset + (2 * stride) + distance),
+ Unsafe.Add(ref samples, q0Offset + (3 * stride) + distance));
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void StoreVector(ref byte samples, int q0Offset, int stride, int distance, Vector128 value)
+ {
+ Unsafe.Add(ref samples, q0Offset + distance) = (byte)value.GetElement(0);
+ Unsafe.Add(ref samples, q0Offset + stride + distance) = (byte)value.GetElement(1);
+ Unsafe.Add(ref samples, q0Offset + (2 * stride) + distance) = (byte)value.GetElement(2);
+ Unsafe.Add(ref samples, q0Offset + (3 * stride) + distance) = (byte)value.GetElement(3);
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LoadScalar(ref byte samples, int q0Offset, int stride, int distance, int index)
+ => Unsafe.Add(ref samples, q0Offset + (index * stride) + distance);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void StoreScalar(ref byte samples, int q0Offset, int stride, int distance, int index, int value)
+ => Unsafe.Add(ref samples, q0Offset + (index * stride) + distance) = (byte)value;
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.VerticalUInt16EdgeOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.VerticalUInt16EdgeOperator.cs
new file mode 100644
index 000000000..707c4226c
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.VerticalUInt16EdgeOperator.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
+
+namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopFilter;
+
+internal static partial class Av1DeblockingFilter
+{
+ ///
+ /// Accesses four rows across a vertical edge in 16-bit storage.
+ ///
+ private readonly struct VerticalUInt16EdgeOperator : IEdgeOperator
+ {
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector128 LoadVector(ref ushort samples, int q0Offset, int stride, int distance)
+ => Vector128.Create(
+ (int)Unsafe.Add(ref samples, q0Offset + distance),
+ Unsafe.Add(ref samples, q0Offset + stride + distance),
+ Unsafe.Add(ref samples, q0Offset + (2 * stride) + distance),
+ Unsafe.Add(ref samples, q0Offset + (3 * stride) + distance));
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void StoreVector(ref ushort samples, int q0Offset, int stride, int distance, Vector128 value)
+ {
+ Unsafe.Add(ref samples, q0Offset + distance) = (ushort)value.GetElement(0);
+ Unsafe.Add(ref samples, q0Offset + stride + distance) = (ushort)value.GetElement(1);
+ Unsafe.Add(ref samples, q0Offset + (2 * stride) + distance) = (ushort)value.GetElement(2);
+ Unsafe.Add(ref samples, q0Offset + (3 * stride) + distance) = (ushort)value.GetElement(3);
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LoadScalar(ref ushort samples, int q0Offset, int stride, int distance, int index)
+ => Unsafe.Add(ref samples, q0Offset + (index * stride) + distance);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void StoreScalar(ref ushort samples, int q0Offset, int stride, int distance, int index, int value)
+ => Unsafe.Add(ref samples, q0Offset + (index * stride) + distance) = (ushort)value;
+ }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.cs
index 04e36a0d7..b74e1b452 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.cs
@@ -16,58 +16,8 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopFilter;
/// lane-wise. Conditional selection preserves unfiltered lanes while allowing four adjacent edge positions to share
/// one kernel invocation.
///
-internal static class Av1DeblockingFilter
+internal static partial class Av1DeblockingFilter
{
- ///
- /// Defines orientation- and storage-specific access to the four samples running along one edge segment.
- ///
- /// The reconstructed sample storage type.
- private interface IEdgeOperator
- where TSample : unmanaged
- {
- ///
- /// Loads four samples at one signed distance across the edge.
- ///
- /// The first element in the plane storage.
- /// The offset of the first Q-side sample.
- /// The number of samples between adjacent rows.
- /// The signed sample distance from Q0.
- /// The widened samples ordered along the edge.
- public static abstract Vector128 LoadVector(ref TSample samples, int q0Offset, int stride, int distance);
-
- ///
- /// Stores four samples at one signed distance across the edge.
- ///
- /// The first element in the plane storage.
- /// The offset of the first Q-side sample.
- /// The number of samples between adjacent rows.
- /// The signed sample distance from Q0.
- /// The widened samples ordered along the edge.
- public static abstract void StoreVector(ref TSample samples, int q0Offset, int stride, int distance, Vector128 value);
-
- ///
- /// Loads one sample at a signed distance across and an offset along the edge.
- ///
- /// The first element in the plane storage.
- /// The offset of the first Q-side sample.
- /// The number of samples between adjacent rows.
- /// The signed sample distance from Q0.
- /// The sample offset along the edge.
- /// The selected sample.
- public static abstract int LoadScalar(ref TSample samples, int q0Offset, int stride, int distance, int index);
-
- ///
- /// Stores one sample at a signed distance across and an offset along the edge.
- ///
- /// The first element in the plane storage.
- /// The offset of the first Q-side sample.
- /// The number of samples between adjacent rows.
- /// The signed sample distance from Q0.
- /// The sample offset along the edge.
- /// The filtered sample.
- public static abstract void StoreScalar(ref TSample samples, int q0Offset, int stride, int distance, int index, int value);
- }
-
///
/// Filters four rows crossing one vertical boundary in eight-bit storage.
///
@@ -943,143 +893,4 @@ internal static class Av1DeblockingFilter
/// The rounded quotient.
private static int RoundPowerOfTwo(int value, int bitCount)
=> (value + (1 << (bitCount - 1))) >> bitCount;
-
- ///
- /// Accesses four rows across a vertical edge in eight-bit storage.
- ///
- private readonly struct VerticalByteEdgeOperator : IEdgeOperator
- {
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Vector128 LoadVector(ref byte samples, int q0Offset, int stride, int distance)
- => Vector128.Create(
- (int)Unsafe.Add(ref samples, q0Offset + distance),
- Unsafe.Add(ref samples, q0Offset + stride + distance),
- Unsafe.Add(ref samples, q0Offset + (2 * stride) + distance),
- Unsafe.Add(ref samples, q0Offset + (3 * stride) + distance));
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void StoreVector(ref byte samples, int q0Offset, int stride, int distance, Vector128 value)
- {
- Unsafe.Add(ref samples, q0Offset + distance) = (byte)value.GetElement(0);
- Unsafe.Add(ref samples, q0Offset + stride + distance) = (byte)value.GetElement(1);
- Unsafe.Add(ref samples, q0Offset + (2 * stride) + distance) = (byte)value.GetElement(2);
- Unsafe.Add(ref samples, q0Offset + (3 * stride) + distance) = (byte)value.GetElement(3);
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int LoadScalar(ref byte samples, int q0Offset, int stride, int distance, int index)
- => Unsafe.Add(ref samples, q0Offset + (index * stride) + distance);
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void StoreScalar(ref byte samples, int q0Offset, int stride, int distance, int index, int value)
- => Unsafe.Add(ref samples, q0Offset + (index * stride) + distance) = (byte)value;
- }
-
- ///
- /// Accesses four columns across a horizontal edge in eight-bit storage.
- ///
- private readonly struct HorizontalByteEdgeOperator : IEdgeOperator
- {
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Vector128 LoadVector(ref byte samples, int q0Offset, int stride, int distance)
- {
- ref byte source = ref Unsafe.Add(ref samples, q0Offset + (distance * stride));
- uint packed = Unsafe.ReadUnaligned(ref source);
- Vector128 widened = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte());
- return Vector128.WidenLower(widened).AsInt32();
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void StoreVector(ref byte samples, int q0Offset, int stride, int distance, Vector128 value)
- {
- Vector128 narrowed16 = Vector128.Narrow(value.AsUInt32(), Vector128.Zero);
- Vector128 narrowed8 = Vector128.Narrow(narrowed16, Vector128.Zero);
- Unsafe.WriteUnaligned(ref Unsafe.Add(ref samples, q0Offset + (distance * stride)), narrowed8.AsUInt32().ToScalar());
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int LoadScalar(ref byte samples, int q0Offset, int stride, int distance, int index)
- => Unsafe.Add(ref samples, q0Offset + (distance * stride) + index);
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void StoreScalar(ref byte samples, int q0Offset, int stride, int distance, int index, int value)
- => Unsafe.Add(ref samples, q0Offset + (distance * stride) + index) = (byte)value;
- }
-
- ///
- /// Accesses four rows across a vertical edge in 16-bit storage.
- ///
- private readonly struct VerticalUInt16EdgeOperator : IEdgeOperator
- {
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Vector128 LoadVector(ref ushort samples, int q0Offset, int stride, int distance)
- => Vector128.Create(
- (int)Unsafe.Add(ref samples, q0Offset + distance),
- Unsafe.Add(ref samples, q0Offset + stride + distance),
- Unsafe.Add(ref samples, q0Offset + (2 * stride) + distance),
- Unsafe.Add(ref samples, q0Offset + (3 * stride) + distance));
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void StoreVector(ref ushort samples, int q0Offset, int stride, int distance, Vector128 value)
- {
- Unsafe.Add(ref samples, q0Offset + distance) = (ushort)value.GetElement(0);
- Unsafe.Add(ref samples, q0Offset + stride + distance) = (ushort)value.GetElement(1);
- Unsafe.Add(ref samples, q0Offset + (2 * stride) + distance) = (ushort)value.GetElement(2);
- Unsafe.Add(ref samples, q0Offset + (3 * stride) + distance) = (ushort)value.GetElement(3);
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int LoadScalar(ref ushort samples, int q0Offset, int stride, int distance, int index)
- => Unsafe.Add(ref samples, q0Offset + (index * stride) + distance);
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void StoreScalar(ref ushort samples, int q0Offset, int stride, int distance, int index, int value)
- => Unsafe.Add(ref samples, q0Offset + (index * stride) + distance) = (ushort)value;
- }
-
- ///
- /// Accesses four columns across a horizontal edge in 16-bit storage.
- ///
- private readonly struct HorizontalUInt16EdgeOperator : IEdgeOperator
- {
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Vector128 LoadVector(ref ushort samples, int q0Offset, int stride, int distance)
- {
- ref ushort source = ref Unsafe.Add(ref samples, q0Offset + (distance * stride));
- ulong packed = Unsafe.ReadUnaligned(ref Unsafe.As(ref source));
- return Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsUInt16()).AsInt32();
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void StoreVector(ref ushort samples, int q0Offset, int stride, int distance, Vector128 value)
- {
- Vector64 narrowed = Vector128.Narrow(value, Vector128.Zero).AsUInt16().GetLower();
- ref byte destination = ref Unsafe.As(ref Unsafe.Add(ref samples, q0Offset + (distance * stride)));
- Unsafe.WriteUnaligned(ref destination, narrowed.AsUInt64().ToScalar());
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int LoadScalar(ref ushort samples, int q0Offset, int stride, int distance, int index)
- => Unsafe.Add(ref samples, q0Offset + (distance * stride) + index);
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void StoreScalar(ref ushort samples, int q0Offset, int stride, int distance, int index, int value)
- => Unsafe.Add(ref samples, q0Offset + (distance * stride) + index) = (ushort)value;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs
index 650d6a21b..3aaa2a358 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs
@@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// Eight-bit blocks use saturating 16-bit stages where their normative ranges permit it; high-bit-depth and scalar
/// fallback paths retain 32-bit stages. Both representations produce the same row-major coefficient contract.
///
-internal static class Av1ForwardTransformer
+internal static partial class Av1ForwardTransformer
{
///
/// Resolves and applies the configured two-dimensional AV1 forward transform.
@@ -65,40 +65,40 @@ internal static class Av1ForwardTransformer
switch (config.TransformFunctionTypeColumn)
{
case Av1TransformFunctionType.Dct4:
- DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
+ DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct8:
- DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
+ DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct16:
- DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
+ DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct32:
- DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
+ DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct64:
- DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
+ DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Adst4:
- DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
+ DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Adst8:
- DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
+ DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Adst16:
- DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
+ DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity4:
- DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
+ DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity8:
- DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
+ DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity16:
- DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
+ DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity32:
- DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
+ DispatchRow(input, coefficients, stride, bitDepth, ref config, workspace);
break;
default:
throw new InvalidImageContentException($"The {config.TransformFunctionTypeColumn} column transform is not valid for {config.TransformSize}.");
@@ -127,40 +127,40 @@ internal static class Av1ForwardTransformer
switch (config.TransformFunctionTypeRow)
{
case Av1TransformFunctionType.Dct4:
- Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
+ Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct8:
- Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
+ Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct16:
- Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
+ Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct32:
- Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
+ Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct64:
- Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
+ Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Adst4:
- Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
+ Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Adst8:
- Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
+ Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Adst16:
- Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
+ Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity4:
- Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
+ Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity8:
- Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
+ Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity16:
- Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
+ Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity32:
- Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
+ Transform2d(input, coefficients, stride, bitDepth, ref config, workspace);
break;
default:
throw new InvalidImageContentException($"The {config.TransformFunctionTypeRow} row transform is not valid for {config.TransformSize}.");
@@ -545,7 +545,7 @@ internal static class Av1ForwardTransformer
///
/// Applies one packed transform axis using the widest efficient lane count available for the block.
///
- /// The transform operator applied to each independent axis.
+ /// The semantic transform operator.
/// The packed transform block.
/// The number of independent axes.
/// The number of packed values between input positions.
@@ -563,23 +563,23 @@ internal static class Av1ForwardTransformer
{
if (Avx512BW.IsSupported && transformCount >= Vector512.Count)
{
- TransformAxis>(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
+ TransformPackedVector512(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
return;
}
if (Avx2.IsSupported && transformCount >= Vector256.Count)
{
- TransformAxis>(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
+ TransformPackedVector256(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
return;
}
- TransformAxis>(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
+ TransformPackedVector128(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
}
///
- /// Applies one signed thirty-two-bit transform axis using the widest efficient lane count available for the block.
+ /// Applies one expanded transform axis using the widest efficient lane count available for the block.
///
- /// The transform operator applied to each independent axis.
+ /// The semantic transform operator.
/// The expanded transform block.
/// The number of independent axes.
/// The number of expanded values between input positions.
@@ -597,66 +597,218 @@ internal static class Av1ForwardTransformer
{
if (Vector512.IsHardwareAccelerated && transformCount >= Vector512.Count)
{
- TransformAxis>(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
+ TransformExpandedVector512(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
return;
}
if (Vector256.IsHardwareAccelerated && transformCount >= Vector256.Count)
{
- TransformAxis>(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
+ TransformExpandedVector256(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
return;
}
if (Vector128.IsHardwareAccelerated && transformCount >= Vector128.Count)
{
- TransformAxis>(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
+ TransformExpandedVector128(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
return;
}
- TransformAxis(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
+ TransformExpandedScalar(buffer, transformCount, inputStride, outputStride, cosBit, workspace);
}
///
- /// Applies one transform stage network to independent axes held in scalar or SIMD lanes.
+ /// Applies a packed transform to thirty-two independent axes.
///
- /// The transform operator applied to each independent axis.
- /// The scalar storage element.
- /// The scalar or SIMD value containing independent transform axes.
- /// The transform block.
- /// The number of independent axes.
- /// The number of storage elements between input positions.
- /// The number of storage elements between output positions.
- /// The fixed-point precision of the cosine constants.
- /// The reusable transform-stage workspace.
- private static void TransformAxis(
- Span buffer,
+ private static void TransformPackedVector512(
+ Span buffer,
+ int transformCount,
+ int inputStride,
+ int outputStride,
+ int cosBit,
+ Span workspace)
+ where TOperator : struct, IAv1ForwardTransform1dOperator
+ {
+ ref Av1TransformVector> buffer0 =
+ ref Unsafe.As>>(ref MemoryMarshal.GetReference(workspace));
+ ref Av1TransformVector> buffer1 =
+ ref Unsafe.Add(ref buffer0, 1);
+ ref short source = ref MemoryMarshal.GetReference(buffer);
+ nint inputByteStride = inputStride * sizeof(short);
+ nint outputByteStride = outputStride * sizeof(short);
+
+ for (int batch = 0; batch < transformCount; batch += Vector512.Count)
+ {
+ ref byte values = ref Unsafe.As(ref Unsafe.Add(ref source, batch));
+
+ TOperator.Transform(ref values, inputByteStride, outputByteStride, ref buffer0, ref buffer1, cosBit);
+ }
+ }
+
+ ///
+ /// Applies a packed transform to sixteen independent axes.
+ ///
+ private static void TransformPackedVector256(
+ Span buffer,
+ int transformCount,
+ int inputStride,
+ int outputStride,
+ int cosBit,
+ Span workspace)
+ where TOperator : struct, IAv1ForwardTransform1dOperator
+ {
+ ref Av1TransformVector> buffer0 =
+ ref Unsafe.As>>(ref MemoryMarshal.GetReference(workspace));
+ ref Av1TransformVector> buffer1 =
+ ref Unsafe.Add(ref buffer0, 1);
+ ref short source = ref MemoryMarshal.GetReference(buffer);
+ nint inputByteStride = inputStride * sizeof(short);
+ nint outputByteStride = outputStride * sizeof(short);
+
+ for (int batch = 0; batch < transformCount; batch += Vector256.Count)
+ {
+ ref byte values = ref Unsafe.As(ref Unsafe.Add(ref source, batch));
+
+ TOperator.Transform(ref values, inputByteStride, outputByteStride, ref buffer0, ref buffer1, cosBit);
+ }
+ }
+
+ ///
+ /// Applies a packed transform to eight independent axes.
+ ///
+ private static void TransformPackedVector128(
+ Span buffer,
+ int transformCount,
+ int inputStride,
+ int outputStride,
+ int cosBit,
+ Span workspace)
+ where TOperator : struct, IAv1ForwardTransform1dOperator
+ {
+ ref Av1TransformVector> buffer0 =
+ ref Unsafe.As>>(ref MemoryMarshal.GetReference(workspace));
+ ref Av1TransformVector> buffer1 =
+ ref Unsafe.Add(ref buffer0, 1);
+ ref short source = ref MemoryMarshal.GetReference(buffer);
+ nint inputByteStride = inputStride * sizeof(short);
+ nint outputByteStride = outputStride * sizeof(short);
+
+ for (int batch = 0; batch < transformCount; batch += Vector128.Count)
+ {
+ ref byte values = ref Unsafe.As(ref Unsafe.Add(ref source, batch));
+
+ TOperator.Transform(ref values, inputByteStride, outputByteStride, ref buffer0, ref buffer1, cosBit);
+ }
+ }
+
+ ///
+ /// Applies an expanded transform to sixteen independent axes.
+ ///
+ private static void TransformExpandedVector512(
+ Span buffer,
+ int transformCount,
+ int inputStride,
+ int outputStride,
+ int cosBit,
+ Span workspace)
+ where TOperator : struct, IAv1ForwardTransform1dOperator
+ {
+ ref Av1TransformVector> buffer0 =
+ ref Unsafe.As>>(ref MemoryMarshal.GetReference(workspace));
+ ref Av1TransformVector> buffer1 =
+ ref Unsafe.Add(ref buffer0, 1);
+ ref int source = ref MemoryMarshal.GetReference(buffer);
+ nint inputByteStride = inputStride * sizeof(int);
+ nint outputByteStride = outputStride * sizeof(int);
+
+ for (int batch = 0; batch < transformCount; batch += Vector512.Count)
+ {
+ ref byte values = ref Unsafe.As(ref Unsafe.Add(ref source, batch));
+
+ TOperator.Transform(ref values, inputByteStride, outputByteStride, ref buffer0, ref buffer1, cosBit);
+ }
+ }
+
+ ///
+ /// Applies an expanded transform to eight independent axes.
+ ///
+ private static void TransformExpandedVector256(
+ Span buffer,
+ int transformCount,
+ int inputStride,
+ int outputStride,
+ int cosBit,
+ Span workspace)
+ where TOperator : struct, IAv1ForwardTransform1dOperator
+ {
+ ref Av1TransformVector