From f8ab68f588fbd675de9a60b7e0ec6e311d3d555e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 27 Aug 2026 09:50:26 +1000 Subject: [PATCH] Document HEIF SIMD architecture --- .../Heif/Av1/Pipeline/Cdef/Av1CdefFilter.cs | 6 +++++ .../Pipeline/FilmGrain/Av1FilmGrainNoise.cs | 23 +++++++++++++++++++ .../Pipeline/FilmGrain/Av1FilmGrainOverlap.cs | 5 ++++ .../FilmGrain/Av1FilmGrainSampleOperator.cs | 12 ++++++++++ .../LoopFilter/Av1DeblockingFilter.cs | 6 +++++ .../Av1SelfGuidedFilter.Operations.cs | 21 +++++++++++++++++ .../LoopRestoration/Av1SelfGuidedFilter.cs | 8 +++++++ .../LoopRestoration/Av1WienerFilter.cs | 6 +++++ .../Av1SuperResolutionFilter.cs | 14 +++++++++++ .../Av1/Prediction/Av1DcIntraPredictor.cs | 9 ++++++++ ...Av1DirectionalIntraPredictor.Operations.cs | 14 ++++++++++- .../Av1DirectionalIntraPredictor.Transpose.cs | 8 ++++++- .../Av1FilterIntraPredictor.Operator.cs | 18 +++++++++++++++ .../Av1IntraPredictor.HorizontalOperator.cs | 3 +++ .../Prediction/Av1IntraPredictor.Operator.cs | 14 +++++++++++ .../Av1IntraPredictor.OperatorContract.cs | 3 +++ .../Prediction/Av1IntraPredictor.Packing.cs | 5 ++++ .../Av1IntraPredictor.PaethOperator.cs | 3 +++ ...IntraPredictor.SmoothHorizontalOperator.cs | 8 +++++++ .../Av1IntraPredictor.SmoothOperator.cs | 8 +++++++ ...v1IntraPredictor.SmoothVerticalOperator.cs | 7 ++++++ .../Av1IntraPredictor.VerticalOperator.cs | 3 +++ .../Av1/Prediction/Av1PalettePredictor.cs | 6 +++++ .../Av1/Prediction/Av1PredictionDecoder.cs | 5 +++- .../Av1ChromaFromLumaContext.Operations.cs | 4 +++- .../Av1ChromaFromLumaPredictor.cs | 6 +++++ .../Av1/Transform/Av1ForwardTransformer.cs | 6 +++++ .../Av1/Transform/Av1IdentityTransform1d.cs | 5 ++++ .../Av1/Transform/Av1Inverse2dTransformer.cs | 18 ++++++++++++++- .../Av1InverseTransformOutputOperator.cs | 9 ++++++++ .../Av1InverseWalshHadamardTransformer.cs | 5 ++++ .../Heif/Av1/Transform/Av1Transform1dMath.cs | 5 ++++ .../Av1/Transform/Av1Transform2dOperations.cs | 6 +++++ .../Heif/Av1/Transform/Av1TransformVector.cs | 10 ++++++-- .../Av1/Transform/Av1TransformWorkspace.cs | 5 ++++ .../Forward/Av1ForwardTransformArithmetic.cs | 16 +++++++++++++ .../IAv1InverseTransformOutputOperator.cs | 5 ++++ .../Av1/Transform/IAv1Transform1dOperator.cs | 7 ++++-- .../Inverse/Av1Adst16Inverse1dOperator.cs | 4 ++++ .../Inverse/Av1Adst4Inverse1dOperator.cs | 4 ++++ .../Inverse/Av1Adst8Inverse1dOperator.cs | 4 ++++ .../Inverse/Av1Dct16Inverse1dOperator.cs | 4 ++++ .../Inverse/Av1Dct32Inverse1dOperator.cs | 4 ++++ .../Inverse/Av1Dct4Inverse1dOperator.cs | 4 ++++ .../Inverse/Av1Dct64Inverse1dOperator.cs | 4 ++++ .../Inverse/Av1Dct8Inverse1dOperator.cs | 4 ++++ .../Inverse/Av1Identity16Inverse1dOperator.cs | 4 ++++ .../Inverse/Av1Identity32Inverse1dOperator.cs | 4 ++++ .../Inverse/Av1Identity4Inverse1dOperator.cs | 4 ++++ .../Inverse/Av1Identity8Inverse1dOperator.cs | 4 ++++ .../HeifColorConverter.CoefficientOperator.cs | 3 +++ ...olorConverter.ConstantLuminanceOperator.cs | 3 +++ .../HeifColorConverter.ICtCpOperator.cs | 3 +++ .../HeifColorConverter.IdentityOperator.cs | 3 +++ .../HeifColorConverter.IptC2Operator.cs | 3 +++ .../HeifColorConverter.Operator.cs | 8 ++++++- .../HeifColorConverter.Smpte2085Operator.cs | 3 +++ .../HeifColorConverter.YCgCoOperator.cs | 3 +++ ...fColorConverter.YCgCoReversibleOperator.cs | 3 +++ .../HeifTransferFunctions.Operator.cs | 5 +++- ...v420ToRgb8Converter.CoefficientOperator.cs | 4 +++- .../HeifYuv420ToRgb8Converter.Operator.cs | 4 +++- .../ColorConverters/IHeifSampleConverter.cs | 5 ++++ .../Formats/Heif/Hevc/HevcDeblockingFilter.cs | 6 +++++ .../Hevc/HevcIntraPredictor.Operations.cs | 10 +++++++- .../Formats/Heif/Hevc/HevcIntraPredictor.cs | 6 +++++ .../Formats/Heif/Hevc/HevcInverseQuantizer.cs | 11 +++++++++ .../Hevc/HevcInverseTransformer.Operations.cs | 15 ++++++++++++ .../Heif/Hevc/HevcResidualReconstructor.cs | 19 +++++++++++++++ .../Hevc/HevcSampleAdaptiveOffsetFilter.cs | 10 ++++++++ 70 files changed, 483 insertions(+), 14 deletions(-) diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.cs index 6b0978d5ab..5e04a01f6e 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Cdef/Av1CdefFilter.cs @@ -12,6 +12,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef; /// /// Applies AV1 constrained directional enhancement filtering and derives the dominant direction of reconstructed blocks. /// +/// +/// Filtering uses signed 16-bit working samples. The 256-bit kernel packs either two complete eight-sample rows or +/// four complete four-sample rows, keeping directional offsets within 128-bit lanes. Direction analysis instead packs +/// 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 { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainNoise.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainNoise.cs index 73668f5ec7..48224ef0c8 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainNoise.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainNoise.cs @@ -12,6 +12,13 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.FilmGrain; /// /// Applies selected AV1 grain blocks to restored luma and chroma samples. /// +/// +/// SIMD lanes follow consecutive samples within one plane row. Grain values and native samples are widened to signed +/// 32-bit lanes before the scaling-table lookup and fixed-point addition, then clipped and narrowed only once. AVX2 +/// uses indexed gathers for the 256-entry scaling table. Portable 128-bit traversal is retained where high-bit-depth +/// interpolation provides enough arithmetic to offset its scalar table reads; all remaining columns use the identical +/// scalar equation. +/// internal static class Av1FilmGrainNoise { /// @@ -186,6 +193,8 @@ internal static class Av1FilmGrainNoise int maximum) where TSample : unmanaged { + // Scaling is an indexed lookup, so AVX2 is selected by gather support rather than generic preferred vector + // width. The portable path is deliberately limited by CanVectorizeWithoutGather for the same reason. if (Avx2.IsSupported) { ApplyLuma( @@ -256,6 +265,9 @@ internal static class Av1FilmGrainNoise int grainRowOffset = row * grainStride; int column = 0; int vectorEnd = width - Vector256.Count; + + // Samples, grain, and scale indices share the same lane coordinate. No permutation is required between + // the lookup, fixed-point multiply, clipping, and native-sample store. for (; column <= vectorEnd; column += Vector256.Count) { ref TSample destination = ref Unsafe.Add(ref sampleBase, sampleRowOffset + column); @@ -308,6 +320,9 @@ internal static class Av1FilmGrainNoise int grainRowOffset = row * grainStride; int column = 0; int vectorEnd = width - Vector128.Count; + + // Four scalar table reads assemble the scale vector; the rest of the normative grain equation remains + // lane-wise, including interpolation for 10- and 12-bit coordinates. for (; column <= vectorEnd; column += Vector128.Count) { ref TSample destination = ref Unsafe.Add(ref sampleBase, sampleRowOffset + column); @@ -402,6 +417,8 @@ internal static class Av1FilmGrainNoise int maximum) where TSample : unmanaged { + // Chroma uses the same indexed scaling-table constraint as luma, so gather support determines the primary + // width and the portable path remains restricted to workloads that amortize scalar table reads. if (Avx2.IsSupported) { ApplyChroma( @@ -554,6 +571,9 @@ internal static class Av1FilmGrainNoise int grainRowOffset = row * grainStride; int column = 0; int vectorEnd = width - Vector256.Count; + + // Each lane represents one chroma coordinate and its corresponding reconstructed-luma coordinate. The Q6 + // luma/chroma blend is clamped to a legal sample code before it becomes a scaling-table index. for (; column <= vectorEnd; column += Vector256.Count) { ref TSample lumaSource = ref Unsafe.Add(ref lumaRow, column << subsamplingX); @@ -682,6 +702,9 @@ internal static class Av1FilmGrainNoise int grainRowOffset = row * grainStride; int column = 0; int vectorEnd = width - Vector128.Count; + + // The four-lane path preserves the same coordinate alignment and Q6 scaling-index arithmetic. Only the + // table read changes from a hardware gather to four scalar reads assembled into a vector. for (; column <= vectorEnd; column += Vector128.Count) { ref TSample lumaSource = ref Unsafe.Add(ref lumaRow, column << subsamplingX); diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainOverlap.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainOverlap.cs index 8e18f43d67..a6aaacc50b 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainOverlap.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainOverlap.cs @@ -11,6 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.FilmGrain; /// /// Blends grain samples across adjacent synthesis blocks. /// +/// +/// Vertical boundaries contain only one or two strided columns and use the fixed scalar kernels. Horizontal boundaries +/// are contiguous and progress from the runtime's preferred native width through smaller vector widths before the +/// scalar tail. Every lane applies the same Q5 overlap weights, rounding offset, and signed grain clamp. +/// internal static class Av1FilmGrainOverlap { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainSampleOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainSampleOperator.cs index 4fae3c7ed4..ad648a7c30 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainSampleOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/FilmGrain/Av1FilmGrainSampleOperator.cs @@ -12,6 +12,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.FilmGrain; /// Loads and stores native AV1 samples for the film-grain arithmetic pipeline. /// /// The native sample type. +/// +/// The decoder closes this operator over for eight-bit planes and for +/// high-bit-depth planes. The JIT removes the unused type branch, so widening and narrowing remain branch-free in the +/// 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 where TSample : unmanaged { @@ -25,6 +31,7 @@ internal readonly struct Av1FilmGrainSampleOperator { if (typeof(TSample) == typeof(byte)) { + // Reading exactly eight bytes avoids touching the following row when the visible width has no padding. ref byte sourceBytes = ref Unsafe.As(ref source); ulong packed = Unsafe.ReadUnaligned(ref sourceBytes); return Avx2.ConvertToVector256Int32(Vector128.CreateScalarUnsafe(packed).AsByte()); @@ -44,6 +51,7 @@ internal readonly struct Av1FilmGrainSampleOperator { if (typeof(TSample) == typeof(byte)) { + // The source occupies only the low four byte lanes; two lower-half widens preserve their original order. ref byte sourceBytes = ref Unsafe.As(ref source); uint packedBytes = Unsafe.ReadUnaligned(ref sourceBytes); Vector128 widened = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packedBytes).AsByte()); @@ -126,6 +134,8 @@ internal readonly struct Av1FilmGrainSampleOperator [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Store8(ref TSample destination, Vector256 values) { + // AddNoise has already clipped every lane to the native sample range. Unsigned narrowing is therefore exact + // and serves only to repack lane width; it does not supply saturation or alter out-of-range values. Vector128 packed = Vector128.Narrow(values.GetLower().AsUInt32(), values.GetUpper().AsUInt32()); if (typeof(TSample) == typeof(byte)) { @@ -146,6 +156,8 @@ internal readonly struct Av1FilmGrainSampleOperator [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Store4(ref TSample destination, Vector128 values) { + // Only four destination samples are valid. Narrow through the low half, then write four bytes or four UInt16 + // values so the store cannot overwrite the next row or an adjacent plane allocation. Vector64 packed = Vector128.Narrow(values.AsUInt32(), Vector128.Zero).GetLower(); if (typeof(TSample) == typeof(byte)) { diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.cs index eb4748e072..04e36a0d77 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1DeblockingFilter.cs @@ -10,6 +10,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopFilter; /// /// Applies the AV1 deblocking kernels to four-sample edge segments. /// +/// +/// Each 32-bit vector lane represents one sample position along the edge. The orientation-specific operators gather or +/// load the corresponding p6..p0,q0..q6 neighborhood, after which masks, flatness tests, and filter equations remain +/// lane-wise. Conditional selection preserves unfiltered lanes while allowing four adjacent edge positions to share +/// one kernel invocation. +/// internal static class Av1DeblockingFilter { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopRestoration/Av1SelfGuidedFilter.Operations.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopRestoration/Av1SelfGuidedFilter.Operations.cs index 6fd1a085d1..c2b3c90238 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopRestoration/Av1SelfGuidedFilter.Operations.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopRestoration/Av1SelfGuidedFilter.Operations.cs @@ -8,6 +8,12 @@ using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopRestoration; +/// +/// Provides the 128- and 256-bit self-guided restoration pipelines. Consecutive lanes represent neighboring output +/// columns throughout integral-image construction, coefficient generation, filtering, and projection. Each vector +/// loop passes its final horizontal prefix to the scalar tail, preserving one continuous summed-area row without +/// recomputing already processed samples. +/// internal static partial class Av1SelfGuidedFilter { /// @@ -37,6 +43,9 @@ internal static partial class Av1SelfGuidedFilter Span scratch, Vector256 vector) { + // The caller-owned span contains two visible filtered planes followed by four identically strided coefficient + // planes. Keeping these regions disjoint allows projection to read either radius after the integral buffers + // have been reused as immutable inputs, without per-unit allocation or copying. int filteredLength = width * height; int bufferLength = GetBufferLength(width, height); int bufferStride = GetBufferStride(width); @@ -134,6 +143,8 @@ internal static partial class Av1SelfGuidedFilter Span scratch, Vector128 vector) { + // Use the same scratch partition as the 256-bit path. Vector width changes only the number of adjacent columns + // advanced by each stage; all offsets and fixed-point representations remain identical. int filteredLength = width * height; int bufferLength = GetBufferLength(width, height); int bufferStride = GetBufferStride(width); @@ -671,6 +682,8 @@ internal static partial class Av1SelfGuidedFilter [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe Vector256 LookupBlendFactors(Vector256 indices) { + // Variance normalization bounds every index to the 256-entry table. AVX2 gather keeps the eight independent + // column lookups in the vector pipeline instead of materializing an intermediate scalar scale buffer. fixed (int* table = XByXPlusOne) { return Avx2.GatherVector256(table, indices.AsInt32(), sizeof(int)); @@ -685,6 +698,8 @@ internal static partial class Av1SelfGuidedFilter [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector128 LookupBlendFactors(Vector128 indices) { + // Portable 128-bit APIs do not provide indexed loads. Four bounded scalar reads are assembled directly into + // the result vector, avoiding both an allocation and a second pass over the coefficient row. ReadOnlySpan table = XByXPlusOne; return Vector128.Create( table[(int)indices.GetElement(0)], @@ -787,6 +802,9 @@ internal static partial class Av1SelfGuidedFilter Vector256 rounding = Vector256.Create(1 << (roundingBits - 1)); int column = 0; int vectorEnd = width - Vector256.Count; + + // Filtered signals use Q4 precision. Projection applies the signaled Q7 weights to their difference from + // the unfiltered Q4 sample, then performs the combined Q11 rounding shift once before clipping. for (; column <= vectorEnd; column += Vector256.Count) { Vector256 factors = CrossSum(blendFactors, coefficientRowOffset + column, bufferStride, row, vector); @@ -843,6 +861,9 @@ internal static partial class Av1SelfGuidedFilter Vector128 rounding = Vector128.Create(1 << (roundingBits - 1)); int column = 0; int vectorEnd = width - Vector128.Count; + + // The 128-bit path uses the same Q4/Q7 projection equation. The four-sample load and store are deliberately + // 64 bits wide so a tightly strided destination row never requires writable padding. for (; column <= vectorEnd; column += Vector128.Count) { Vector128 factors = CrossSum(blendFactors, coefficientRowOffset + column, bufferStride, row, vector); diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopRestoration/Av1SelfGuidedFilter.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopRestoration/Av1SelfGuidedFilter.cs index b1f173e087..54b3d7899f 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopRestoration/Av1SelfGuidedFilter.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopRestoration/Av1SelfGuidedFilter.cs @@ -10,6 +10,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopRestoration; /// /// Applies the normative AV1 self-guided restoration filter and projection. /// +/// +/// The accelerated paths process adjacent output columns in SIMD lanes and use the caller-provided scratch span for +/// filtered samples, local coefficients, and padded integral images. AVX2 is selected where variance-to-blend lookup +/// can use a native gather; the portable 128-bit path performs four scalar table reads while retaining vectorized +/// window and projection arithmetic. The scalar path uses the same fixed-point units and scratch partition. +/// internal static partial class Av1SelfGuidedFilter { /// @@ -151,6 +157,8 @@ internal static partial class Av1SelfGuidedFilter ReadOnlySpan projectionCoefficients, Span scratch) { + // The closed vector overloads share the same scratch layout and fixed-point equations. AVX2 is preferred here + // because its coefficient stage can gather four noncontiguous entries from the 256-value blend table. if (Avx2.IsSupported) { FilterBlock( diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopRestoration/Av1WienerFilter.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopRestoration/Av1WienerFilter.cs index cb51e8e8f2..735dd0a065 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopRestoration/Av1WienerFilter.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopRestoration/Av1WienerFilter.cs @@ -11,6 +11,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopRestoration; /// /// Applies the normative separable Wiener filter used by AV1 loop restoration. /// +/// +/// The horizontal pass consumes one contiguous eight-tap window at a time. SIMD pairwise multiply-add widens adjacent +/// unsigned-sample and signed-coefficient products into four 32-bit partial sums, which are reduced before normative +/// rounding and clipping. The resulting caller-owned intermediate plane supplies contiguous columns to the vertical +/// pass without allocating per stripe. +/// internal static class Av1WienerFilter { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/SuperResolution/Av1SuperResolutionFilter.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/SuperResolution/Av1SuperResolutionFilter.cs index fc8aecda95..05b25c5601 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/SuperResolution/Av1SuperResolutionFilter.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/SuperResolution/Av1SuperResolutionFilter.cs @@ -13,6 +13,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.SuperResolution; /// /// Applies the normative horizontal filter used by AV1 super-resolution upscaling. /// +/// +/// Four output coordinates are evaluated together, but each coordinate starts as its own eight-lane vector of source +/// taps. Pairwise multiply-add produces four partial sums per output, and two horizontal reductions transpose those +/// four independent dot products into consecutive output lanes. This layout supports arbitrary fixed-point source +/// steps and filter phases without gathers or a temporary coefficient matrix. +/// internal static class Av1SuperResolutionFilter { /// @@ -245,6 +251,9 @@ internal static class Av1SuperResolutionFilter GetOffsets(sourcePosition + (step * 3), out int sourceOffset3, out int filterOffset3); ref short filter = ref MemoryMarshal.GetReference(Filters); + + // Replicated edge storage guarantees all eight taps are contiguous even for the first and last output. Exact + // 64-bit reads avoid depending on additional row padding before widening each tap set to signed Int16 lanes. Vector128 samples0 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(Unsafe.ReadUnaligned(ref Unsafe.Add(ref source, sourceOffset0))).AsByte()).AsInt16(); Vector128 samples1 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(Unsafe.ReadUnaligned(ref Unsafe.Add(ref source, sourceOffset1))).AsByte()).AsInt16(); Vector128 samples2 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(Unsafe.ReadUnaligned(ref Unsafe.Add(ref source, sourceOffset2))).AsByte()).AsInt16(); @@ -276,6 +285,9 @@ internal static class Av1SuperResolutionFilter GetOffsets(sourcePosition + (step * 3), out int sourceOffset3, out int filterOffset3); ref short filter = ref MemoryMarshal.GetReference(Filters); + + // AV1 high-bit-depth samples are at most twelve bits, so their signed Int16 view remains positive. Keeping the + // tap and coefficient types equal enables the same pairwise multiply-add reduction as the eight-bit path. return FilterFour( Vector128.LoadUnsafe(ref source, (nuint)sourceOffset0).AsInt16(), Vector128.LoadUnsafe(ref source, (nuint)sourceOffset1).AsInt16(), @@ -348,6 +360,8 @@ internal static class Av1SuperResolutionFilter return Vector128.Create(leftPairs, rightPairs); } + // The portable fallback selects even and odd lanes separately. ShuffleNative is valid here because every index + // is in range; adding the two permutations forms [x0+x1, x2+x3] for each source vector. Vector128 evenIndices = Vector128.Create(0, 2, 0, 2); Vector128 oddIndices = Vector128.Create(1, 3, 1, 3); Vector128 leftPairsFallback = Vector128.ShuffleNative(left, evenIndices) + Vector128.ShuffleNative(left, oddIndices); diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcIntraPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcIntraPredictor.cs index b316bfed4c..7168c56b4f 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcIntraPredictor.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcIntraPredictor.cs @@ -10,6 +10,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; /// /// Reconstructs AV1 DC intra-prediction blocks from the available neighboring samples. /// +/// +/// Reference reduction uses the widest available integer lanes, then one rounded scalar mean is broadcast across each +/// destination row. Row filling is delegated to span operations so the runtime supplies its native vectorized store; +/// explicit scalar entry points remain available for feature-disabled conformance tests. +/// internal static class Av1DcIntraPredictor { /// @@ -124,6 +129,8 @@ internal static class Av1DcIntraPredictor int sum = 0; int index = 0; + // Widening prevents the packed-byte reduction from overflowing before the horizontal sum. The shared index + // advances through every available vector width and leaves only the incomplete final group to the scalar loop. if (Vector512.IsHardwareAccelerated) { int oneVectorFromEnd = samples.Length - Vector512.Count; @@ -173,6 +180,8 @@ internal static class Av1DcIntraPredictor int sum = 0; int index = 0; + // Signed 16-bit storage is nonnegative for supported bit depths. Widening to Int32 preserves the exact sum of + // the largest permitted reference edge before the rounded mean is calculated once outside this loop. if (Vector512.IsHardwareAccelerated) { int oneVectorFromEnd = samples.Length - Vector512.Count; diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalIntraPredictor.Operations.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalIntraPredictor.Operations.cs index 026f7449cb..26b8d8834e 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalIntraPredictor.Operations.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalIntraPredictor.Operations.cs @@ -8,7 +8,11 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; /// -/// Provides packed projection kernels for AV1 directional intra prediction. +/// Provides packed projection kernels for AV1 directional intra prediction. Contiguous zone-one references map one +/// output sample to each lane. Upsampled references use native byte or 16-bit table shuffles to select alternating +/// half-sample positions, while zone-two left projections construct four independent coordinates per vector because +/// their bases advance by the directional derivative rather than by a fixed memory stride. Every interpolation uses +/// Q5 weights and the scalar continuation preserves the same rounding and endpoint-extension rules. /// internal static partial class Av1DirectionalIntraPredictor { @@ -189,6 +193,8 @@ internal static partial class Av1DirectionalIntraPredictor if (!upsample) { + // A single index is advanced through all supported widths. A narrower path consumes only the remainder + // left by the wider path, so the row is written once without requiring padded destination storage. if (Vector512.IsHardwareAccelerated) { int oneVectorFromEnd = validCount - Vector512.Count; @@ -240,6 +246,8 @@ internal static partial class Av1DirectionalIntraPredictor if (Vector128.IsHardwareAccelerated) { + // Four-lane construction covers both the final non-upsampled remainder and targets without a native + // gather. Each lane carries an independently projected coordinate but shares the row's interpolation weight. int oneVectorFromEnd = validCount - 4; for (; index <= oneVectorFromEnd; index += 4) { @@ -293,6 +301,8 @@ internal static partial class Av1DirectionalIntraPredictor if (!upsample) { + // High-bit-depth samples stay in signed 16-bit storage, but interpolation widens to Int32 before the Q5 + // weighted sum. The largest supported 12-bit sample therefore cannot overflow an intermediate lane. if (Vector512.IsHardwareAccelerated) { int oneVectorFromEnd = validCount - Vector512.Count; @@ -391,6 +401,8 @@ internal static partial class Av1DirectionalIntraPredictor if (Vector128.IsHardwareAccelerated) { + // Zone-two left references are not contiguous across output columns. Constructing the four source pairs + // directly avoids a temporary gather-index buffer and keeps the scalar continuation at the same offset. int oneVectorFromEnd = destination.Length - 4; for (; index <= oneVectorFromEnd; index += 4) { diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalIntraPredictor.Transpose.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalIntraPredictor.Transpose.cs index c980d833f1..06b6c530bd 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalIntraPredictor.Transpose.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalIntraPredictor.Transpose.cs @@ -9,7 +9,10 @@ using SixLabors.ImageSharp.Common.Helpers; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; /// -/// Provides SIMD block transposition for zone 3 directional prediction. +/// Provides SIMD block transposition for zone 3 directional prediction. AV1 block dimensions are multiples of four, +/// allowing the traversal to use complete eight-by-eight tiles where possible and complete four-by-four tiles for the +/// remaining small blocks. Unpack stages exchange coordinate bits inside registers; exact-width loads and stores keep +/// every access within the logical block even when the destination has no writable row padding. /// internal static partial class Av1DirectionalIntraPredictor { @@ -25,6 +28,8 @@ internal static partial class Av1DirectionalIntraPredictor { if (Vector128.IsHardwareAccelerated) { + // Selecting one tile size for the complete block keeps both loop increments aligned with the AV1 block + // dimensions. No partial SIMD tile reaches a neighboring prediction block. int tileSize = sourceWidth >= 8 && sourceHeight >= 8 ? 8 : 4; for (int y = 0; y < sourceHeight; y += tileSize) { @@ -65,6 +70,7 @@ internal static partial class Av1DirectionalIntraPredictor { if (Vector128.IsHardwareAccelerated) { + // The same tiling invariant applies to two-byte samples; only the register unpack granularity differs. int tileSize = sourceWidth >= 8 && sourceHeight >= 8 ? 8 : 4; for (int y = 0; y < sourceHeight; y += tileSize) { diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1FilterIntraPredictor.Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1FilterIntraPredictor.Operator.cs index 005ed33e26..0b5098a384 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1FilterIntraPredictor.Operator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1FilterIntraPredictor.Operator.cs @@ -8,12 +8,21 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; +/// +/// Provides recursive filter-intra traversal for the closed coefficient operators. +/// internal abstract partial class Av1FilterIntraPredictorBase { /// /// Applies one closed filter-intra coefficient operator using the widest useful SIMD width. /// /// The filter-intra coefficient set. + /// + /// AV1 filter-intra predicts a two-row by four-column group from seven samples that may include previously + /// predicted groups. The fixed-stride scratch surface preserves those dependencies with a one-sample top and left + /// border. SIMD lanes hold the eight outputs of one group in row-major order; they do not span independent groups, + /// because the next group can depend on the values just produced. + /// internal sealed class Av1FilterIntraPredictor : Av1FilterIntraPredictorBase where TOperator : struct, IAv1FilterIntraPredictionOperator { @@ -32,6 +41,8 @@ internal abstract partial class Av1FilterIntraPredictorBase if (Vector256.IsHardwareAccelerated) { + // Each tap vector contains the coefficient at one tap position for the eight row-major outputs in a + // 2-by-4 group. Broadcasting the seven reconstructed inputs therefore evaluates all outputs together. Vector256 tap0 = CreateTapVector256(ref taps, 0); Vector256 tap1 = CreateTapVector256(ref taps, 1); Vector256 tap2 = CreateTapVector256(ref taps, 2); @@ -70,6 +81,8 @@ internal abstract partial class Av1FilterIntraPredictorBase } else if (Vector128.IsHardwareAccelerated) { + // A 128-bit vector covers one four-sample output row. Low and high coefficient vectors describe the + // first and second rows respectively while sharing the same seven reconstructed input broadcasts. Vector128 tap0Low = CreateTapVector128(ref taps, 0, 0); Vector128 tap1Low = CreateTapVector128(ref taps, 1, 0); Vector128 tap2Low = CreateTapVector128(ref taps, 2, 0); @@ -128,6 +141,8 @@ internal abstract partial class Av1FilterIntraPredictorBase if (Vector256.IsHardwareAccelerated) { + // High-bit-depth storage changes only the final clamp and narrowing. The Int32 accumulator layout is + // identical to the eight-bit path, preserving all signed coefficient products before Q4 rounding. Vector256 tap0 = CreateTapVector256(ref taps, 0); Vector256 tap1 = CreateTapVector256(ref taps, 1); Vector256 tap2 = CreateTapVector256(ref taps, 2); @@ -245,6 +260,8 @@ internal abstract partial class Av1FilterIntraPredictorBase /// The shared top-left reference. private static void Initialize(Span buffer, ReadOnlySpan above, ReadOnlySpan left, int width, int height, byte topLeft) { + // Predictions use one-based coordinates in the workspace. Row zero and column zero retain the prepared + // references while later groups overwrite only the interior values on which following groups depend. buffer[0] = topLeft; above[..width].CopyTo(buffer[1..]); for (int row = 0; row < height; row++) @@ -264,6 +281,7 @@ internal abstract partial class Av1FilterIntraPredictorBase /// The shared top-left reference. private static void Initialize(Span buffer, ReadOnlySpan above, ReadOnlySpan left, int width, int height, short topLeft) { + // Match the eight-bit one-based workspace so the recursive source offsets remain representation-agnostic. buffer[0] = topLeft; above[..width].CopyTo(buffer[1..]); for (int row = 0; row < height; row++) diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.HorizontalOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.HorizontalOperator.cs index 1781e88df0..a831d80e2e 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.HorizontalOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.HorizontalOperator.cs @@ -6,6 +6,9 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; +/// +/// Provides horizontal intra prediction for scalar and SIMD sample representations. +/// internal abstract partial class Av1IntraPredictorBase { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.Operator.cs index cd4ca5eae4..5cb5640d37 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.Operator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.Operator.cs @@ -7,12 +7,20 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; +/// +/// Provides the width-progressive SIMD traversal shared by closed non-directional prediction operators. +/// internal abstract partial class Av1IntraPredictorBase { /// /// Applies one closed non-directional AV1 prediction operator using the widest available SIMD width. /// /// The prediction-mode-specific arithmetic. + /// + /// Each lane produces one output column. Top samples and column weights vary by lane, while the current row's + /// left sample and row weight are broadcast. declares which references it uses; + /// because the operator type is closed, the JIT can remove unused loads and broadcasts from each prediction mode. + /// internal sealed class Av1IntraPredictor : Av1IntraPredictorBase where TOperator : struct, IAv1IntraPredictionOperator { @@ -41,6 +49,8 @@ internal abstract partial class Av1IntraPredictorBase byte bottomLeft = usesBottomLeft ? Unsafe.Add(ref leftBase, height - 1) : default; int processedColumns = 0; + // Widths are cumulative rather than mutually exclusive. A wide vector advances the row prefix, then the + // narrower paths consume any complete vectors left before the scalar tail handles the final columns. if (Vector512.IsHardwareAccelerated) { int vectorizedColumns = width - (width % Vector512.Count); @@ -168,6 +178,8 @@ internal abstract partial class Av1IntraPredictorBase short bottomLeft = usesBottomLeft ? Unsafe.Add(ref leftBase, height - 1) : default; int processedColumns = 0; + // High-bit-depth samples use signed storage but remain nonnegative. Each vector lane follows one output + // column, so the same width-progressive traversal is valid without inter-lane packing or saturation. if (Vector512.IsHardwareAccelerated) { int vectorizedColumns = width - (width % Vector512.Count); @@ -256,6 +268,8 @@ internal abstract partial class Av1IntraPredictorBase } } + // Retain a scalar continuation for widths smaller than the available vectors and for forced-scalar test + // execution. AV1 block dimensions keep this tail short during normal hardware-accelerated decoding. for (int row = 0; row < height; row++) { short leftSample = usesLeft ? Unsafe.Add(ref leftBase, row) : default; diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.OperatorContract.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.OperatorContract.cs index deb1854851..1533747b94 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.OperatorContract.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.OperatorContract.cs @@ -5,6 +5,9 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; +/// +/// Defines the neighbor-usage flags and scalar/SIMD contract for closed intra-prediction operators. +/// internal abstract partial class Av1IntraPredictorBase { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.Packing.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.Packing.cs index a0faad29d0..4414925e2c 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.Packing.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.Packing.cs @@ -9,6 +9,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; /// /// Widens AV1 sample vectors for fixed-point prediction arithmetic and narrows completed results. /// +/// +/// Widening preserves consecutive sample order while splitting packed vectors into equal low-to-high groups. Callers +/// perform interpolation in signed 32-bit lanes and clip before narrowing, so the unsigned byte overloads may use +/// unsigned narrowing and the high-bit-depth overloads may use signed narrowing without additional saturation logic. +/// internal abstract partial class Av1IntraPredictorBase { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.PaethOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.PaethOperator.cs index 05277e1c38..733d3b1608 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.PaethOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.PaethOperator.cs @@ -6,6 +6,9 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; +/// +/// Provides Paeth intra prediction for scalar and SIMD sample representations. +/// internal abstract partial class Av1IntraPredictorBase { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.SmoothHorizontalOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.SmoothHorizontalOperator.cs index 9256b69471..05f9cba748 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.SmoothHorizontalOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.SmoothHorizontalOperator.cs @@ -6,11 +6,19 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; +/// +/// Provides horizontal smooth intra prediction for scalar and SIMD sample representations. +/// internal abstract partial class Av1IntraPredictorBase { /// /// Implements horizontal AV1 smooth intra prediction for scalar and SIMD lanes. /// + /// + /// Each lane uses its Q8 column weight to interpolate between the current row's left sample and the top-right + /// endpoint. Rewriting the complementary weight around 256 leaves one multiply per lane plus a shared endpoint and + /// rounding bias. + /// internal readonly struct SmoothHorizontalOperator : IAv1IntraPredictionOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.SmoothOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.SmoothOperator.cs index d56183ff17..c7774a28d3 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.SmoothOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.SmoothOperator.cs @@ -6,11 +6,19 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; +/// +/// Provides two-dimensional smooth intra prediction for scalar and SIMD sample representations. +/// internal abstract partial class Av1IntraPredictorBase { /// /// Implements two-dimensional AV1 smooth intra prediction for scalar and SIMD lanes. /// + /// + /// Horizontal and vertical Q8 interpolations are accumulated before one Q9 rounding shift. Expanding each + /// complementary weight around 256 reduces the lane equation to two products and a shared endpoint bias while + /// preserving the normative result exactly. + /// internal readonly struct SmoothOperator : IAv1IntraPredictionOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.SmoothVerticalOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.SmoothVerticalOperator.cs index 37f6dedb7a..e7b5c7acdc 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.SmoothVerticalOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.SmoothVerticalOperator.cs @@ -6,11 +6,18 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; +/// +/// Provides vertical smooth intra prediction for scalar and SIMD sample representations. +/// internal abstract partial class Av1IntraPredictorBase { /// /// Implements vertical AV1 smooth intra prediction for scalar and SIMD lanes. /// + /// + /// The row's Q8 weight is common to every lane, while top references vary by column. Rewriting the complementary + /// weight around 256 leaves one product per lane plus a broadcast bottom-left endpoint and rounding bias. + /// internal readonly struct SmoothVerticalOperator : IAv1IntraPredictionOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.VerticalOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.VerticalOperator.cs index 954767caba..d02857261a 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.VerticalOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1IntraPredictor.VerticalOperator.cs @@ -6,6 +6,9 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; +/// +/// Provides vertical intra prediction for scalar and SIMD sample representations. +/// internal abstract partial class Av1IntraPredictorBase { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PalettePredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PalettePredictor.cs index 7913a4732c..21d648a3e6 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PalettePredictor.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PalettePredictor.cs @@ -10,6 +10,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; /// /// Reconstructs AV1 palette-predicted sample blocks from decoded color-index maps. /// +/// +/// AV1 palettes contain at most eight entries, so a complete 8-bit palette fits in each native shuffle lane and a +/// complete high-bit-depth palette fits as sixteen bytes. Color indices become byte-shuffle controls; replicating the +/// table per 128-bit lane keeps every lookup lane-local at 128, 256, and 512 bits. Exact-width tail loads and stores +/// avoid requiring writable padding around small transform blocks. +/// internal static class Av1PalettePredictor { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionDecoder.cs index d3df5bdce6..79d50aa07b 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionDecoder.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionDecoder.cs @@ -17,7 +17,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; /// Reconstructs AV1 intra-predicted transform blocks from neighboring samples and decoded mode information. /// /// -/// This type implements the intra prediction portion of the AV1 reconstruction process for 8-, 10-, and 12-bit samples. +/// This type implements the intra prediction portion of the AV1 reconstruction process for 8-, 10-, and 12-bit +/// samples. Intra-edge filtering and upsampling operate on caller-owned padded scratch: adjacent reference samples map +/// to adjacent SIMD lanes, exact-width stores interleave filtered half samples with the original edge, and scalar +/// continuations handle only incomplete vectors. The completed edges then feed the closed prediction operators. /// internal class Av1PredictionDecoder { diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaContext.Operations.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaContext.Operations.cs index a85f28657b..d2376fbcf4 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaContext.Operations.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaContext.Operations.cs @@ -12,7 +12,9 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.ChromaFromLuma; /// -/// Provides packed luma subsampling and mean-subtraction operations for chroma-from-luma prediction. +/// Provides packed luma subsampling and mean-subtraction operations for chroma-from-luma prediction. Consecutive lanes +/// represent chroma coordinates. Horizontal and vertical luma sums are converted directly to Q3, after which a vector +/// reduction derives the common rounded mean and lane-wise subtraction leaves the zero-mean AC predictor surface. /// internal partial class Av1ChromaFromLumaContext { diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaPredictor.cs index ccdf34ca98..5a64654774 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaPredictor.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaPredictor.cs @@ -12,6 +12,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.ChromaFromLuma; /// /// Applies an AV1 chroma-from-luma residual to a DC-predicted chroma block. /// +/// +/// Each signed 16-bit lane contains one zero-mean Q3 luma value. Packed rounded-high multiplication converts the product +/// with the Q3 alpha parameter directly to a signed integer adjustment; the alpha/luma sign mask restores the product +/// sign after the magnitude operation. The common DC prediction is broadcast, then results are clipped and narrowed to +/// the destination sample representation. +/// internal static class Av1ChromaFromLumaPredictor { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs index 5ff9464ea3..650d6a21b2 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs @@ -12,6 +12,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; /// /// Converts spatial residual samples into AV1 transform coefficients. /// +/// +/// The SIMD pipeline transposes rows into lanes before invoking the one-dimensional operators. One vector then holds +/// the same transform position from several independent axes, allowing the complete stage network to run lane-wise. +/// 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 { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1IdentityTransform1d.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1IdentityTransform1d.cs index aad957b91d..fad1e3f539 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1IdentityTransform1d.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1IdentityTransform1d.cs @@ -9,6 +9,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; /// /// Provides the shared SIMD arithmetic for AV1 identity-transform operators. /// +/// +/// Transform-vector fields represent positions along an identity transform and lanes represent independent axes. +/// Scaling is lane-local; a zero fractional-bit count uses exact multiplication, while fixed-point variants use the +/// same rounded butterfly primitive as DCT and ADST operators. +/// internal static class Av1IdentityTransform1d { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs index ca54711f83..12f5a131f4 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs @@ -11,6 +11,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; /// /// Applies separable two-dimensional AV1 inverse transforms and reconstructs decoded samples. /// +/// +/// Coefficients are transposed so that each SIMD lane represents an independent transform axis and each vector field +/// represents one coefficient position. The column and row operators can then use the scalar stage graph without +/// cross-lane permutations. Reconstruction adds the final residuals to their matching prediction lanes before +/// narrowing to the decoded sample depth. +/// internal static class Av1Inverse2dTransformer { /// @@ -320,6 +326,9 @@ internal static class Av1Inverse2dTransformer byte rowClampBits = (byte)(bitDepth + 8); byte columnClampBits = (byte)Math.Max(bitDepth + 6, 16); + // Three transform vectors occupy the fixed prefix of the caller-owned workspace. Reinterpreting that storage + // gives constant field offsets to the one-dimensional operators; the remaining raster buffer holds the first + // axis result without allocating or aliasing any active stage vector. ref int workspaceBase = ref MemoryMarshal.GetReference(workspace); ref Av1TransformVector> tempIn = ref Unsafe.As>>(ref workspaceBase); ref Av1TransformVector> tempOut = ref Unsafe.As>>(ref Unsafe.Add(ref workspaceBase, vectorLength)); @@ -404,7 +413,8 @@ internal static class Av1Inverse2dTransformer ref TSample readBase = ref MemoryMarshal.GetReference(outputForRead); ref TSample writeBase = ref MemoryMarshal.GetReference(outputForWrite); - // The intermediate rows already contain contiguous column groups, avoiding a second transpose. + // The intermediate rows already contain contiguous column groups, avoiding a second transpose. Horizontal and + // vertical flips are folded into these loads and row selections so flipped transforms need no reversal pass. for (int column = 0; column < width; column += laneCount) { int sourceColumn = config.FlipLeftToRight ? width - column - laneCount : column; @@ -472,6 +482,8 @@ internal static class Av1Inverse2dTransformer byte rowClampBits = (byte)(bitDepth + 8); byte columnClampBits = (byte)Math.Max(bitDepth + 6, 16); + // The 128-bit workspace has the same three-vector plus raster-buffer layout as the 256-bit path. Only the + // number of independent axes represented by each vector changes from eight to four. ref int workspaceBase = ref MemoryMarshal.GetReference(workspace); ref Av1TransformVector> tempIn = ref Unsafe.As>>(ref workspaceBase); ref Av1TransformVector> tempOut = ref Unsafe.As>>(ref Unsafe.Add(ref workspaceBase, vectorLength)); @@ -480,6 +492,8 @@ internal static class Av1Inverse2dTransformer ref int inputBase = ref MemoryMarshal.GetReference(input); ref int bufferBase = ref MemoryMarshal.GetReference(buffer); + // A 4-by-4 transpose changes four raster rows into four coefficient-position vectors. Each lane then remains + // one independent row throughout the complete first-axis stage network. for (int row = 0; row < height; row += laneCount) { for (int column = 0; column < width; column += laneCount) @@ -531,6 +545,8 @@ internal static class Av1Inverse2dTransformer ref TSample readBase = ref MemoryMarshal.GetReference(outputForRead); ref TSample writeBase = ref MemoryMarshal.GetReference(outputForWrite); + // Contiguous four-column groups become the independent lanes for the second axis. Flip selection is applied + // while reading the intermediate block and selecting completed rows, avoiding any extra copy or reversal. for (int column = 0; column < width; column += laneCount) { int sourceColumn = config.FlipLeftToRight ? width - column - laneCount : column; diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformOutputOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformOutputOperator.cs index d121ff0fb4..5b6fa5c93f 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformOutputOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformOutputOperator.cs @@ -10,6 +10,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; /// /// Reconstructs AV1 samples from predicted values and inverse-transform residuals. /// +/// +/// Each SIMD lane represents one consecutive reconstructed sample. Packed byte or 16-bit predictions are widened to +/// signed 32-bit lanes before residual addition, clipped to the coded sample range, and narrowed into exact-width +/// stores. The closed specialization removes storage-type branches from hot loops. +/// /// The decoded sample storage type. internal readonly struct Av1InverseTransformOutputOperator : IAv1InverseTransformOutputOperator where TSample : unmanaged @@ -36,6 +41,8 @@ internal readonly struct Av1InverseTransformOutputOperator : IAv1Invers { if (typeof(TSample) == typeof(byte)) { + // Read and write exactly four bytes. The unused upper lanes only participate in narrowing and never reach + // memory, which keeps reconstruction valid at a tightly packed row boundary. ref byte source = ref Unsafe.As(ref prediction); uint packed = Unsafe.ReadUnaligned(ref source); Vector128 predicted16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte()); @@ -63,6 +70,8 @@ internal readonly struct Av1InverseTransformOutputOperator : IAv1Invers { if (typeof(TSample) == typeof(byte)) { + // Eight byte predictions widen through UInt16 into the eight Int32 residual lanes. The final 64-bit store + // covers only those reconstructed samples and does not require destination padding. ref byte source = ref Unsafe.As(ref prediction); ulong packed = Unsafe.ReadUnaligned(ref source); Vector128 predicted16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte()); diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseWalshHadamardTransformer.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseWalshHadamardTransformer.cs index 344f44f001..034e1d5a41 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseWalshHadamardTransformer.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseWalshHadamardTransformer.cs @@ -10,6 +10,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; /// /// Applies the reversible four-by-four inverse Walsh-Hadamard transform used by lossless AV1 segments. /// +/// +/// The vector path stores one transform row in each and one column position in each lane. +/// Register transposes exchange the two transform dimensions between identical reversible butterflies. Reconstruction +/// then adds four consecutive residual lanes to each prediction row with exact-width output stores. +/// internal static class Av1InverseWalshHadamardTransformer { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform1dMath.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform1dMath.cs index a478abdf1a..148cee2d5a 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform1dMath.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform1dMath.cs @@ -10,6 +10,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; /// /// Provides the fixed-point arithmetic shared by the scalar and SIMD AV1 one-dimensional transform kernels. /// +/// +/// The general transform path keeps one independent axis in each signed 32-bit lane. Low-bit-depth forward transforms +/// additionally use signed 16-bit lanes and whole-butterfly AVX2 or AVX-512 multiply-add operations, matching AV1's +/// stage saturation points before packing. Scalar overloads preserve the same rounding and serve as the fallback. +/// internal static class Av1Transform1dMath { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dOperations.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dOperations.cs index f45a803548..954c5a86b8 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dOperations.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dOperations.cs @@ -12,6 +12,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; /// /// Provides the SIMD data-layout operations shared by AV1 two-dimensional transforms. /// +/// +/// The one-dimensional operators expect one transform position per vector and one independent axis per lane. These +/// routines transpose rectangular sample tiles into that structure, then transpose the completed axes back to raster +/// order. Every shuffle is consequently an index-bit exchange between row and column coordinates; it does not alter +/// the signed fixed-point sample representation. +/// internal static class Av1Transform2dOperations { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformVector.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformVector.cs index 75435474a4..3bc8198a30 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformVector.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformVector.cs @@ -11,12 +11,18 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; /// Stores the fixed set of SIMD values used by one bulk AV1 transform axis. /// /// The SIMD vector type used for parallel transform lanes. +/// +/// Field Vn stores transform position n for every independent axis in the vector lanes. A +/// therefore carries four 32-bit axes, while a +/// carries eight. Transform stages operate vertically through +/// these fields and never mix lanes, so their scalar fixed-point rounding and clamping rules remain unchanged. +/// [StructLayout(LayoutKind.Sequential)] internal struct Av1TransformVector where TVector : struct { - // Explicit fields give the JIT constant offsets inside large transform operators. The previous inline-array - // helpers were not inlined once those operators exceeded the JIT's expansion budget, causing a call per access. + // Explicit fields make each transform position a constant field offset. An indexed inline-array accessor was not + // expanded inside the larger stage networks, which introduced a helper call for every coefficient access. public TVector V0; public TVector V1; public TVector V2; diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformWorkspace.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformWorkspace.cs index bc5d3ff3f3..f07fb8e9a0 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformWorkspace.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformWorkspace.cs @@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; /// /// Describes the reusable storage required by an AV1 two-dimensional transform operation. /// +/// +/// The workspace reserves three maximum-length transform vectors for input, output, and stage exchange, followed by +/// full-block raster buffers and the fixed AVX-512 transpose area. All storage is expressed as integers so callers can +/// rent one buffer and reinterpret aligned prefixes for 16-bit or 32-bit SIMD lanes without per-block allocations. +/// internal static class Av1TransformWorkspace { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1ForwardTransformArithmetic.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1ForwardTransformArithmetic.cs index 341d62100d..ed4a120b8c 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1ForwardTransformArithmetic.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1ForwardTransformArithmetic.cs @@ -11,6 +11,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; /// Provides the sample-type and vector-width arithmetic used by the shared AV1 forward-transform stage networks. /// /// The scalar or SIMD value containing independent transform axes. +/// +/// Each closed is either one scalar axis or a vector of independent axes. The +/// branches are resolved when the generic type is compiled, so they select arithmetic once +/// without adding per-stage runtime dispatch. Signed 16-bit representations use the saturating operations required by +/// the packed transform pipeline; 32-bit representations retain the normative wrapping fixed-point arithmetic. +/// internal static class Av1ForwardTransformArithmetic where TValue : struct { @@ -25,6 +31,8 @@ internal static class Av1ForwardTransformArithmetic int value = 1 << (cosBit - 1); Av1TransformRounding rounding = default; + // The closed TValue makes this a compile-time shape selection. Only the matching explicit-layout field is + // initialized and subsequently read, keeping the broadcast outside every butterfly in the stage network. if (typeof(TValue) == typeof(Vector128) || typeof(TValue) == typeof(Vector128)) { rounding.Vector128 = Vector128.Create(value); @@ -740,6 +748,8 @@ internal static class Av1ForwardTransformArithmetic int cosBit, Vector512 rounding) { + // Unpacking forms adjacent (input0, input1) pairs independently inside each 128-bit lane. Pairwise multiply-add + // widens those pairs to Int32 for rounding, and the final pack restores original lane order with saturation. Vector512 lowerInputs = Avx512BW.UnpackLow(input0, input1); Vector512 upperInputs = Avx512BW.UnpackHigh(input0, input1); Vector512 weights = Avx512BW.UnpackLow(Vector512.Create((short)weight0), Vector512.Create((short)weight1)); @@ -764,6 +774,8 @@ internal static class Av1ForwardTransformArithmetic int cosBit, Vector128 rounding) { + // Four products can exceed Int16 even though the completed stage value cannot. Widening each input first keeps + // the full fixed-point sum until rounding; explicit clamping supplies the required saturating demotion. (Vector128 input0Lower, Vector128 input0Upper) = Vector128.Widen(input0); (Vector128 input1Lower, Vector128 input1Upper) = Vector128.Widen(input1); (Vector128 input2Lower, Vector128 input2Upper) = Vector128.Widen(input2); @@ -804,6 +816,8 @@ internal static class Av1ForwardTransformArithmetic int cosBit, Vector256 rounding) { + // The two unpack streams contain alternating input pairs for the lower and upper lane groups. Adding the two + // pairwise products completes each four-term dot product before the rounded saturating pack restores Int16. Vector256 weights01 = Avx2.UnpackLow(Vector256.Create((short)weight0), Vector256.Create((short)weight1)); Vector256 weights23 = Avx2.UnpackLow(Vector256.Create((short)weight2), Vector256.Create((short)weight3)); Vector256 lower = Avx2.MultiplyAddAdjacent(Avx2.UnpackLow(input0, input1), weights01) @@ -833,6 +847,8 @@ internal static class Av1ForwardTransformArithmetic int cosBit, Vector512 rounding) { + // AVX-512BW preserves the same lane-local pair layout as the 256-bit path. Two pairwise dot products form each + // four-term result in Int32, after which rounding and signed saturation return thirty-two independent axes. Vector512 weights01 = Avx512BW.UnpackLow(Vector512.Create((short)weight0), Vector512.Create((short)weight1)); Vector512 weights23 = Avx512BW.UnpackLow(Vector512.Create((short)weight2), Vector512.Create((short)weight3)); Vector512 lower = Avx512BW.MultiplyAddAdjacent(Avx512BW.UnpackLow(input0, input1), weights01) diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/IAv1InverseTransformOutputOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/IAv1InverseTransformOutputOperator.cs index f9238be0ce..7e2244cc6e 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/IAv1InverseTransformOutputOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/IAv1InverseTransformOutputOperator.cs @@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; /// /// Defines how inverse-transform residuals are added to a decoded sample representation. /// +/// +/// Residual lanes correspond to consecutive reconstructed samples. Implementations must widen packed predictions, +/// add and clip in signed 32-bit lanes, then store exactly four or eight results so callers do not require writable +/// padding beyond the transform block. The closed sample type allows byte and high-bit-depth storage to specialize. +/// /// The decoded sample storage type. internal interface IAv1InverseTransformOutputOperator where TSample : unmanaged diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/IAv1Transform1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/IAv1Transform1dOperator.cs index 81f39ccd0e..09ecb8a3b6 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/IAv1Transform1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/IAv1Transform1dOperator.cs @@ -9,8 +9,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; /// Defines the scalar and SIMD arithmetic for one AV1 one-dimensional transform. /// /// -/// Each overload performs the same transform. The two-dimensional traversal selects a concrete operator and the -/// widest supported lane width once per block, allowing the JIT to specialize every static interface call. +/// Each overload performs the same staged fixed-point transform. In the SIMD overloads, each vector field identifies +/// one coefficient position and each lane identifies an independent row or column. Butterfly arithmetic is therefore +/// lane-local: vectorization changes only how many axes advance together, not coefficient order, rounding, or stage +/// clamping. The two-dimensional traversal selects the concrete operator and lane width once per block, allowing the +/// JIT to specialize every static interface call outside the stage network. /// internal interface IAv1Transform1dOperator { diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst16Inverse1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst16Inverse1dOperator.cs index c0d6b43452..04f0aff06c 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst16Inverse1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst16Inverse1dOperator.cs @@ -8,6 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; /// /// Defines the 16-point AV1 inverse asymmetric discrete sine transform operator. /// +/// +/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply +/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes. +/// internal readonly partial struct Av1Adst16Inverse1dOperator : IAv1Transform1dOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst4Inverse1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst4Inverse1dOperator.cs index 86b0cbdd73..fef59237f9 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst4Inverse1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst4Inverse1dOperator.cs @@ -8,6 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; /// /// Defines the four-point AV1 inverse asymmetric discrete sine transform operator. /// +/// +/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply +/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes. +/// internal readonly partial struct Av1Adst4Inverse1dOperator : IAv1Transform1dOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst8Inverse1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst8Inverse1dOperator.cs index 6a24b9841c..d6978dadfb 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst8Inverse1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst8Inverse1dOperator.cs @@ -8,6 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; /// /// Defines the eight-point AV1 inverse asymmetric discrete sine transform operator. /// +/// +/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply +/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes. +/// internal readonly partial struct Av1Adst8Inverse1dOperator : IAv1Transform1dOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct16Inverse1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct16Inverse1dOperator.cs index e6f9344989..9659fd51e4 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct16Inverse1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct16Inverse1dOperator.cs @@ -8,6 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; /// /// Defines the 16-point AV1 inverse discrete cosine transform operator. /// +/// +/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply +/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes. +/// internal readonly partial struct Av1Dct16Inverse1dOperator : IAv1Transform1dOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct32Inverse1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct32Inverse1dOperator.cs index 865070aade..62eaadbb9b 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct32Inverse1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct32Inverse1dOperator.cs @@ -8,6 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; /// /// Defines the 32-point AV1 inverse discrete cosine transform operator. /// +/// +/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply +/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes. +/// internal readonly partial struct Av1Dct32Inverse1dOperator : IAv1Transform1dOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct4Inverse1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct4Inverse1dOperator.cs index 469699137a..9b6705fffe 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct4Inverse1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct4Inverse1dOperator.cs @@ -8,6 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; /// /// Defines the four-point AV1 inverse discrete cosine transform operator. /// +/// +/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply +/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes. +/// internal readonly partial struct Av1Dct4Inverse1dOperator : IAv1Transform1dOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct64Inverse1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct64Inverse1dOperator.cs index 34d274e043..2ab33cb19e 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct64Inverse1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct64Inverse1dOperator.cs @@ -8,6 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; /// /// Defines the 64-point AV1 inverse discrete cosine transform operator. /// +/// +/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply +/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes. +/// internal readonly partial struct Av1Dct64Inverse1dOperator : IAv1Transform1dOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct8Inverse1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct8Inverse1dOperator.cs index a79fda0269..49df841241 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct8Inverse1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct8Inverse1dOperator.cs @@ -8,6 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; /// /// Defines the eight-point AV1 inverse discrete cosine transform operator. /// +/// +/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply +/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes. +/// internal readonly partial struct Av1Dct8Inverse1dOperator : IAv1Transform1dOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity16Inverse1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity16Inverse1dOperator.cs index ec4f42d307..8bdcb5bda2 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity16Inverse1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity16Inverse1dOperator.cs @@ -8,6 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; /// /// Defines the sixteen-point AV1 inverse identity transform operator. /// +/// +/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local, +/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis. +/// internal readonly partial struct Av1Identity16Inverse1dOperator : IAv1Transform1dOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity32Inverse1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity32Inverse1dOperator.cs index 0d0baf7a82..7af8b05e79 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity32Inverse1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity32Inverse1dOperator.cs @@ -8,6 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; /// /// Defines the thirty-two-point AV1 inverse identity transform operator. /// +/// +/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local, +/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis. +/// internal readonly partial struct Av1Identity32Inverse1dOperator : IAv1Transform1dOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity4Inverse1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity4Inverse1dOperator.cs index a97b340864..d9abda95b6 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity4Inverse1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity4Inverse1dOperator.cs @@ -8,6 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; /// /// Defines the four-point AV1 inverse identity transform operator. /// +/// +/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local, +/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis. +/// internal readonly partial struct Av1Identity4Inverse1dOperator : IAv1Transform1dOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity8Inverse1dOperator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity8Inverse1dOperator.cs index c6029f1daa..0a9a339273 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity8Inverse1dOperator.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity8Inverse1dOperator.cs @@ -8,6 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; /// /// Defines the eight-point AV1 inverse identity transform operator. /// +/// +/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local, +/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis. +/// internal readonly partial struct Av1Identity8Inverse1dOperator : IAv1Transform1dOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.CoefficientOperator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.CoefficientOperator.cs index e7956d10c4..60dda9f47c 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.CoefficientOperator.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.CoefficientOperator.cs @@ -6,6 +6,9 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Components; +/// +/// Provides coefficient-matrix YCbCr conversion for scalar and SIMD lanes. +/// internal abstract partial class HeifColorConverterBase { /// diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.ConstantLuminanceOperator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.ConstantLuminanceOperator.cs index 591cf61009..98ad00352c 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.ConstantLuminanceOperator.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.ConstantLuminanceOperator.cs @@ -6,6 +6,9 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Components; +/// +/// Provides constant-luminance YCbCr conversion for scalar and SIMD lanes. +/// internal abstract partial class HeifColorConverterBase { /// diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.ICtCpOperator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.ICtCpOperator.cs index 45fbd2d8a3..af37f6d5ed 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.ICtCpOperator.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.ICtCpOperator.cs @@ -7,6 +7,9 @@ using SixLabors.ImageSharp.Metadata.Profiles.Cicp; namespace SixLabors.ImageSharp.Formats.Heif.Components; +/// +/// Provides ICtCp transfer-domain matrix conversion for scalar and SIMD lanes. +/// internal abstract partial class HeifColorConverterBase { /// diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.IdentityOperator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.IdentityOperator.cs index f563e1d564..80bafa8f44 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.IdentityOperator.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.IdentityOperator.cs @@ -6,6 +6,9 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Components; +/// +/// Provides identity-matrix component reordering for scalar and SIMD color conversion. +/// internal abstract partial class HeifColorConverterBase { /// diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.IptC2Operator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.IptC2Operator.cs index be5781149b..8abd23afe8 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.IptC2Operator.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.IptC2Operator.cs @@ -6,6 +6,9 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Components; +/// +/// Provides IPT-C2 transfer-domain matrix conversion for scalar and SIMD lanes. +/// internal abstract partial class HeifColorConverterBase { /// diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.Operator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.Operator.cs index 5906a0e2af..9b118a3112 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.Operator.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.Operator.cs @@ -8,13 +8,19 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Components; /// -/// Provides the static operator contract and SIMD traversal used by HEIF color converters. +/// Provides the static operator contract and SIMD traversal used by HEIF color converters. Each lane carries one pixel +/// and the three vectors remain planar component rows throughout conversion. Descending vector widths consume a single +/// shared offset, preserving SIMD execution for the remainder without overlapping stores or requiring row padding. /// internal abstract partial class HeifColorConverterBase { /// /// Defines color-model arithmetic for scalar and SIMD lanes in both conversion directions. /// + /// + /// Operator methods are lane-local and must preserve input order. The closed operator type lets the JIT bind the + /// matrix or lifting transform once per converter, keeping color-model dispatch outside every row loop. + /// internal interface IHeifColorOperator { /// diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.Smpte2085Operator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.Smpte2085Operator.cs index de4f51d147..451c167121 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.Smpte2085Operator.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.Smpte2085Operator.cs @@ -6,6 +6,9 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Components; +/// +/// Provides SMPTE ST 2085 YDzDx conversion for scalar and SIMD lanes. +/// internal abstract partial class HeifColorConverterBase { /// diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.YCgCoOperator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.YCgCoOperator.cs index 35c50fcf07..414985887b 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.YCgCoOperator.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.YCgCoOperator.cs @@ -6,6 +6,9 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Components; +/// +/// Provides YCgCo matrix conversion for scalar and SIMD lanes. +/// internal abstract partial class HeifColorConverterBase { /// diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.YCgCoReversibleOperator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.YCgCoReversibleOperator.cs index 3ba72bedaa..015ffdc4c1 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.YCgCoReversibleOperator.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifColorConverter.YCgCoReversibleOperator.cs @@ -6,6 +6,9 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Components; +/// +/// Provides reversible YCgCo integer lifting for scalar and SIMD lanes. +/// internal abstract partial class HeifColorConverterBase { /// diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifTransferFunctions.Operator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifTransferFunctions.Operator.cs index f20529882d..db908d0df2 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifTransferFunctions.Operator.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifTransferFunctions.Operator.cs @@ -8,7 +8,10 @@ using static SixLabors.ImageSharp.Formats.Heif.Components.HeifTransferVectorOper namespace SixLabors.ImageSharp.Formats.Heif.Components; /// -/// Provides fixed-width vector overloads and shared H.273 transfer operations for HEIF color conversion. +/// Provides fixed-width vector overloads and shared H.273 transfer operations for HEIF color conversion. One lane +/// represents one normalized color component. Closed vector operators bind the 128-, 256-, or 512-bit implementation +/// once per row kernel, while conditional selection evaluates piecewise transfer curves without per-lane branches. +/// Inputs to logarithms and powers are bounded before evaluation because SIMD selection evaluates both branches. /// internal static partial class HeifTransferFunctions { diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.CoefficientOperator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.CoefficientOperator.cs index a693156525..7945220beb 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.CoefficientOperator.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.CoefficientOperator.cs @@ -7,7 +7,9 @@ using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.Formats.Heif.Components; /// -/// Provides full-range fixed-point coefficient conversion. +/// Provides full-range fixed-point coefficient conversion. Each signed 32-bit lane carries one luma or duplicated +/// chroma sample. Matrix coefficients use a common fixed-point scale; every component rounds once after its complete +/// weighted sum, then clips before the enclosing row kernel narrows and packs the RGB result. /// internal static partial class HeifYuv420ToRgb8Converter { diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.Operator.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.Operator.cs index a692b8eda8..c881188bb5 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.Operator.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/HeifYuv420ToRgb8Converter.Operator.cs @@ -9,7 +9,9 @@ using SixLabors.ImageSharp.Common.Helpers; namespace SixLabors.ImageSharp.Formats.Heif.Components; /// -/// Defines fixed-point operators and row traversal for eight-bit 4:2:0 conversion. +/// Defines fixed-point operators and row traversal for eight-bit 4:2:0 conversion. Consecutive lanes represent output +/// pixels; each native chroma sample is duplicated into the two lanes covered by horizontal subsampling before the +/// closed color operator runs. All products remain in signed 32-bit lanes until clipped RGB values are narrowed. /// internal static partial class HeifYuv420ToRgb8Converter { diff --git a/src/ImageSharp/Formats/Heif/Components/ColorConverters/IHeifSampleConverter.cs b/src/ImageSharp/Formats/Heif/Components/ColorConverters/IHeifSampleConverter.cs index 686da1dd0b..6ee0b02e2b 100644 --- a/src/ImageSharp/Formats/Heif/Components/ColorConverters/IHeifSampleConverter.cs +++ b/src/ImageSharp/Formats/Heif/Components/ColorConverters/IHeifSampleConverter.cs @@ -10,6 +10,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Components; /// Defines SIMD widening and narrowing operations for one native HEIF sample representation. /// /// The native sample type. +/// +/// Every vector lane represents one consecutive planar sample. Implementations widen native integer storage to +/// normalized single-precision arithmetic and narrow only values already scaled and clipped for the destination sample +/// type. The containing row traversal selects vector width; these operators do not reorder pixels or cross row bounds. +/// internal interface IHeifSampleConverter where TSample : unmanaged { diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.cs index 3c115d4e3e..cb4c0e32fa 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcDeblockingFilter.cs @@ -9,6 +9,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Hevc; /// /// Applies the HEVC luma and chroma deblocking kernels to four-sample edge segments. /// +/// +/// Each 32-bit lane represents one position along an edge segment. Orientation-specific operators gather the samples +/// at a common signed distance across that edge; the luma and chroma masks and adjustments then execute lane-wise. +/// Loads with fewer than four valid positions populate only the low lanes, which the matching store writes without +/// touching samples beyond the picture boundary. +/// internal static class HevcDeblockingFilter { /// diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.Operations.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.Operations.cs index 2e4c4c8169..c75c17b0ae 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.Operations.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.Operations.cs @@ -10,7 +10,10 @@ using SixLabors.ImageSharp.Common.Helpers; namespace SixLabors.ImageSharp.Formats.Heif.Hevc; /// -/// Provides shared SIMD operations used by the closed prediction operators. +/// Provides shared SIMD operations used by the closed prediction operators. Each lane represents one output column; +/// planar and angular interpolation widen 16-bit references before their Q5 weighted sums, then narrow only after the +/// normative rounding shift. Horizontal prediction reuses the vertical row kernel through a caller-owned contiguous +/// block and an eight-by-eight transpose, keeping the arithmetic identical without gathering strided destination rows. /// internal static partial class HevcIntraPredictor { @@ -120,6 +123,9 @@ internal static partial class HevcIntraPredictor ref ushort samplesBase = ref MemoryMarshal.GetReference(samples); uint sum = 0; int i = 0; + + // Widen before reduction because a complete 64-sample, 12-bit reference edge exceeds UInt16. The shared index + // lets narrower vectors consume only the remainder from the widest available path. if (Vector512.IsHardwareAccelerated) { int oneVectorFromEnd = samples.Length - Vector512.Count; @@ -275,6 +281,8 @@ internal static partial class HevcIntraPredictor uint rightWeight = (uint)fraction; int i = 0; + // Adjacent source vectors overlap by one sample, aligning each left/right reference pair in the same lane. + // Widening keeps the largest 12-bit Q5 weighted sum below the UInt32 limit before narrowing to sample storage. if (Vector512.IsHardwareAccelerated) { int oneVectorFromEnd = destination.Length - Vector512.Count; diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.cs index b79ee9db15..81d3004577 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.cs @@ -12,6 +12,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Hevc; /// /// Reconstructs HEVC intra-prediction blocks from prepared neighboring samples. /// +/// +/// Closed static operators select planar, DC, or angular arithmetic once per block. SIMD rows keep neighboring output +/// columns in consecutive lanes; broadcast left endpoints and vector top references then evaluate the interpolation +/// without per-sample mode dispatch. Horizontal angular prediction reuses the vertical kernel in contiguous scratch +/// storage and transposes once into the strided destination. +/// internal static partial class HevcIntraPredictor { /// diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseQuantizer.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseQuantizer.cs index 6330160eac..24bcadec4b 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseQuantizer.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseQuantizer.cs @@ -11,6 +11,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Hevc; /// /// Reconstructs dequantized HEVC transform coefficients. /// +/// +/// Consecutive quantized coefficients occupy consecutive 32-bit lanes. Scaling-list values are widened to the same lane +/// shape before the inverse scale, quantization shift, rounding, and transform-range clamp are applied. Vector-width +/// loops advance the complete coefficient prefix and leave only the final incomplete group to the scalar equation. +/// internal static class HevcInverseQuantizer { /// @@ -130,6 +135,8 @@ internal static class HevcInverseQuantizer ref int destinationBase = ref MemoryMarshal.GetReference(destination); int i = 0; + // Descending widths advance one shared coefficient offset. Smaller registers consume complete groups left by a + // wider path, so the scalar loop sees fewer than four values without any overlapping dequantization stores. if (Vector512.IsHardwareAccelerated) { int oneVectorFromEnd = source.Length - Vector512.Count; @@ -197,6 +204,8 @@ internal static class HevcInverseQuantizer ref byte matrixBase = ref MemoryMarshal.GetReference(matrix); int i = 0; + // Scaling matrices use one unsigned byte per coefficient. Each width loads exactly its matching byte count, + // widens in source order, and multiplies by the common inverse-quantization scale before signed arithmetic. if (Vector512.IsHardwareAccelerated) { int oneVectorFromEnd = source.Length - Vector512.Count; @@ -304,6 +313,8 @@ internal static class HevcInverseQuantizer int outputMinimum, int outputMaximum) { + // A positive rightShift applies nearest-integer rounding before division. A nonpositive value represents an + // exact left shift; the earlier input clamp guarantees that multiplication and shifting remain in Int32 range. values = Vector512.Clamp(values, Vector512.Create(inputMinimum), Vector512.Create(inputMaximum)); Vector512 result = values * weights; result = rightShift > 0 diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.Operations.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.Operations.cs index fb5805cc27..7ebc4c2130 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.Operations.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcInverseTransformer.Operations.cs @@ -8,6 +8,11 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; namespace SixLabors.ImageSharp.Formats.Heif.Hevc; +/// +/// Provides the shared HEVC inverse-transform stage and transpose operations. Vector lanes represent independent +/// transform lines, while consecutive scratch rows represent frequency groups in the partial-butterfly factorization. +/// Arithmetic never mixes lines; transposition is the only operation that exchanges row and column coordinates. +/// internal static partial class HevcInverseTransformer { /// @@ -77,6 +82,8 @@ internal static partial class HevcInverseTransformer ref int destinationBase = ref MemoryMarshal.GetReference(destination); int x = 0; + // Source storage is frequency-major: advancing one lane moves to the same frequency in another independent + // transform line. The shared X offset lets each narrower width continue exactly where the wider loop stopped. if (Vector512.IsHardwareAccelerated) { int oneVectorFromEnd = lineCount - Vector512.Count; @@ -168,6 +175,8 @@ internal static partial class HevcInverseTransformer ReadOnlySpan even = currentIsInitial ? initial : alternate; Span destination = currentIsInitial ? alternate : initial; + // Odd rows remain in the initial disjoint-group buffer while expanded even rows alternate buffers. This + // preserves every source row needed by later hierarchy levels without allocating another transform block. for (int position = 0; position < combinedSize; position++) { ReadOnlySpan evenRow = even.Slice((combinedStart + position) * lineCount, lineCount); @@ -385,6 +394,9 @@ internal static partial class HevcInverseTransformer { ref int sourceBase = ref MemoryMarshal.GetReference(source); ref int destinationBase = ref MemoryMarshal.GetReference(destination); + + // Every supported HEVC transform dimension is a multiple of four. Complete four-by-four tiles therefore + // transpose the rectangular block without masked loads, partial stores, or access to row padding. for (int y = 0; y < sourceHeight; y += 4) { for (int x = 0; x < sourceWidth; x += 4) @@ -435,6 +447,8 @@ internal static partial class HevcInverseTransformer if (Vector256.IsHardwareAccelerated) { + // Eight UInt16 predictions widen into one Int32 vector so residual addition cannot overflow sample + // storage. Narrowing occurs only after clipping and stores exactly the eight logical destination values. int oneVectorFromEnd = width - Vector256.Count; for (; x <= oneVectorFromEnd; x += Vector256.Count) { @@ -451,6 +465,7 @@ internal static partial class HevcInverseTransformer if (Vector128.IsHardwareAccelerated) { + // The four-sample path uses exact 64-bit loads and stores; it does not depend on writable row padding. int oneVectorFromEnd = width - Vector128.Count; for (; x <= oneVectorFromEnd; x += Vector128.Count) { diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.cs index 13827bd413..c981c82094 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcResidualReconstructor.cs @@ -10,6 +10,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Hevc; /// /// Reconstructs HEVC transform-skipped, bypassed, and differential residual blocks. /// +/// +/// Consecutive residual samples are widened to signed 32-bit lanes for normalization and prediction addition. Closed +/// static operators encode the selected transform-skip shift so the JIT specializes left-shift, rounded-right-shift, +/// and identity cases outside the row loops. Saturation to the residual range and clipping to sample depth occur at the +/// same stage in every vector width and in the scalar tail. +/// internal static class HevcResidualReconstructor { /// @@ -287,6 +293,8 @@ internal static class HevcResidualReconstructor int count = coefficients.Length; int index = 0; + // Rotation reverses the complete raster sequence, not the lanes of independently loaded forward chunks. Each + // load therefore starts at the mirrored chunk and shuffles its lanes before the common destination traversal. if (Vector512.IsHardwareAccelerated) { for (; index <= count - Vector512.Count; index += Vector512.Count) @@ -333,6 +341,8 @@ internal static class HevcResidualReconstructor int count = source.Length; int index = 0; + // The descending source loads and ascending destination stores never overlap because callers provide distinct + // coefficient and residual spans. The shared index permits a scalar tail for non-vector-sized blocks. if (Vector512.IsHardwareAccelerated) { for (; index <= count - Vector512.Count; index += Vector512.Count) @@ -433,6 +443,9 @@ internal static class HevcResidualReconstructor { ref int residualBase = ref MemoryMarshal.GetReference(residual); int x = 0; + + // Lanes are independent columns. Carrying the reconstructed row above in the accumulator removes the need for + // a horizontal shuffle while preserving the top-to-bottom dependency of residual DPCM. if (Vector512.IsHardwareAccelerated) { Vector512 minimum = Vector512.Create(ResidualMinimum); @@ -508,6 +521,8 @@ internal static class HevcResidualReconstructor int x = 0; int accumulator = 0; + // PrefixSum resolves dependencies inside a vector. The final lane then seeds the next vector width or the + // scalar tail, so changing SIMD width cannot change the left-to-right accumulation order. if (Vector512.IsHardwareAccelerated) { Vector512 minimum = Vector512.Create(ResidualMinimum); @@ -564,6 +579,8 @@ internal static class HevcResidualReconstructor [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector512 PrefixSum(Vector512 values) { + // Out-of-range shuffle indices create zero lanes. Distances 1, 2, 4, and 8 form an inclusive Hillis-Steele + // scan without carrying values backward across the start of the vector. values += Vector512.Shuffle(values, Vector512.Create(16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)); values += Vector512.Shuffle(values, Vector512.Create(16, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)); values += Vector512.Shuffle(values, Vector512.Create(16, 16, 16, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); @@ -578,6 +595,7 @@ internal static class HevcResidualReconstructor [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector256 PrefixSum(Vector256 values) { + // Out-of-range index eight supplies the zero lanes needed at each doubling step. values += Vector256.Shuffle(values, Vector256.Create(8, 0, 1, 2, 3, 4, 5, 6)); values += Vector256.Shuffle(values, Vector256.Create(8, 8, 0, 1, 2, 3, 4, 5)); return values + Vector256.Shuffle(values, Vector256.Create(8, 8, 8, 8, 0, 1, 2, 3)); @@ -591,6 +609,7 @@ internal static class HevcResidualReconstructor [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector128 PrefixSum(Vector128 values) { + // Out-of-range index four supplies the zero lanes needed at distances one and two. values += Vector128.Shuffle(values, Vector128.Create(4, 0, 1, 2)); return values + Vector128.Shuffle(values, Vector128.Create(4, 4, 0, 1)); } diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.cs index 0fee497a6d..7dda714e8e 100644 --- a/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.cs +++ b/src/ImageSharp/Formats/Heif/Hevc/HevcSampleAdaptiveOffsetFilter.cs @@ -10,6 +10,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Hevc; /// /// Applies HEVC sample-adaptive offsets to reconstructed component blocks. /// +/// +/// Each SIMD lane classifies one reconstructed sample. Band-offset operators derive the class directly from the current +/// value, while edge-offset operators compare aligned lanes from the two neighboring coordinates. The resulting class +/// indices select one of the signaled offsets, after which addition and bit-depth clipping remain lane-wise. A scalar +/// continuation handles only incomplete vectors at picture edges. +/// internal static class HevcSampleAdaptiveOffsetFilter { /// @@ -508,6 +514,8 @@ internal static class HevcSampleAdaptiveOffsetFilter [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector512 SelectOffset(Vector512 classes, in KernelParameters kernel) { + // The table contains only five values and there is no portable 16-bit gather. A comparison chain keeps every + // class lane in registers and leaves unrecognized classes at the required zero offset. Vector512 selected = Vector512.Zero; selected = Vector512.ConditionalSelect(Vector512.Equals(classes, Vector512.Create((short)0)), Vector512.Create(kernel.Offset0), selected); selected = Vector512.ConditionalSelect(Vector512.Equals(classes, Vector512.Create((short)1)), Vector512.Create(kernel.Offset1), selected); @@ -625,6 +633,8 @@ internal static class HevcSampleAdaptiveOffsetFilter Vector512 neighbor1, in KernelParameters kernel) { + // Each comparison pair produces -1, 0, or 1. Adding two maps the normative edge classes onto the + // contiguous zero-through-four offset-table indices used by the selection kernel. Vector512 one = Vector512.Create((short)1); Vector512 sign0 = (Vector512.GreaterThan(current, neighbor0) & one) - (Vector512.LessThan(current, neighbor0) & one); Vector512 sign1 = (Vector512.GreaterThan(current, neighbor1) & one) - (Vector512.LessThan(current, neighbor1) & one);