From e06a015cf59148e24d1fc940aed3b9e4d8356103 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 7 Apr 2026 11:39:29 +1000 Subject: [PATCH] Fix SIMD slicing and padding length handling. Fix #3104 --- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 14 ++++++++------ .../Encoder/SpectralConverter{TPixel}.cs | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index ff5ea5de33..076590605d 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -752,7 +752,7 @@ internal static partial class SimdUtils /// Implementation is based on MagicScaler code: /// https://github.com/saucecontrol/PhotoSauce/blob/b5811908041200488aa18fdfd17df5fc457415dc/src/MagicScaler/Magic/Processors/ConvertersFloat.cs#L80-L182 /// - internal static unsafe void ByteToNormalizedFloat( + internal static void ByteToNormalizedFloat( ReadOnlySpan source, Span destination) { @@ -1172,8 +1172,10 @@ internal static partial class SimdUtils Vector256 rgb, rg, bx; Vector256 r, g, b; + // Each iteration consumes 8 Rgb24 pixels (24 bytes) but starts with a 32-byte load, + // so we need 3 extra pixels of addressable slack beyond the vectorized chunk. const int bytesPerRgbStride = 24; - nuint count = (uint)source.Length / 8; + nuint count = source.Length > 3 ? (uint)(source.Length - 3) / 8 : 0; for (nuint i = 0; i < count; i++) { rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (uint)(bytesPerRgbStride * i)).AsUInt32(), extractToLanesMask).AsByte(); @@ -1193,10 +1195,10 @@ internal static partial class SimdUtils } int sliceCount = (int)(count * 8); - redChannel = redChannel.Slice(sliceCount); - greenChannel = greenChannel.Slice(sliceCount); - blueChannel = blueChannel.Slice(sliceCount); - source = source.Slice(sliceCount); + redChannel = redChannel[sliceCount..]; + greenChannel = greenChannel[sliceCount..]; + blueChannel = blueChannel[sliceCount..]; + source = source[sliceCount..]; } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs index b60ef68f11..8662c5c49b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs @@ -114,9 +114,9 @@ internal class SpectralConverter : SpectralConverter, IDisposable Span sourceRow = this.pixelBuffer.DangerousGetRowSpan(srcIndex); PixelOperations.Instance.UnpackIntoRgbPlanes(rLane, gLane, bLane, sourceRow); - rLane.Slice(paddingStartIndex).Fill(rLane[paddingStartIndex - 1]); - gLane.Slice(paddingStartIndex).Fill(gLane[paddingStartIndex - 1]); - bLane.Slice(paddingStartIndex).Fill(bLane[paddingStartIndex - 1]); + rLane.Slice(paddingStartIndex, paddedPixelsCount).Fill(rLane[paddingStartIndex - 1]); + gLane.Slice(paddingStartIndex, paddedPixelsCount).Fill(gLane[paddingStartIndex - 1]); + bLane.Slice(paddingStartIndex, paddedPixelsCount).Fill(bLane[paddingStartIndex - 1]); // Convert from rgb24 to target pixel type JpegColorConverterBase.ComponentValues values = new(this.componentProcessors, y);