From a4ad7b09fc8dda2f0efa9e0f7abcf4e6f0a4ac67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Sun, 26 Mar 2023 14:20:24 +0200 Subject: [PATCH 1/6] Avoid length-check in pinning spans Only where it seems profitable. E.g. not when a UnmanagedMemoryStream is constructed of that pointer. --- .../ColorSpaces/Companding/SRgbCompanding.cs | 4 ++-- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 2 +- src/ImageSharp/Compression/Zlib/Adler32.cs | 2 +- src/ImageSharp/Compression/Zlib/Crc32.cs | 6 +++--- src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs | 9 ++++----- .../Formats/Webp/Lossless/PredictorEncoder.cs | 10 +++++----- src/ImageSharp/Formats/Webp/WebpCommonUtils.cs | 4 ++-- src/ImageSharp/Memory/Buffer2DExtensions.cs | 2 +- 8 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs index 2a6fcb0832..4c3923c888 100644 --- a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs @@ -167,7 +167,7 @@ public static float Compress(float channel) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe void CompandAvx2(Span vectors, float[] table) { - fixed (float* tablePointer = &table[0]) + fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) { var scale = Vector256.Create((float)Scale); Vector256 zero = Vector256.Zero; @@ -199,7 +199,7 @@ private static unsafe void CompandAvx2(Span vectors, float[] table) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe void CompandScalar(Span vectors, float[] table) { - fixed (float* tablePointer = &table[0]) + fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) { Vector4 zero = Vector4.Zero; var scale = new Vector4(Scale); diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index ce6f335a8f..7871eee6ba 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -644,7 +644,7 @@ internal static unsafe void ByteToNormalizedFloat( ReadOnlySpan source, Span dest) { - fixed (byte* sourceBase = source) + fixed (byte* sourceBase = &MemoryMarshal.GetReference(source)) { if (Avx2.IsSupported) { diff --git a/src/ImageSharp/Compression/Zlib/Adler32.cs b/src/ImageSharp/Compression/Zlib/Adler32.cs index dd8217541a..3ecdf81539 100644 --- a/src/ImageSharp/Compression/Zlib/Adler32.cs +++ b/src/ImageSharp/Compression/Zlib/Adler32.cs @@ -387,7 +387,7 @@ private static unsafe uint CalculateScalar(uint adler, ReadOnlySpan buffer uint s1 = adler & 0xFFFF; uint s2 = (adler >> 16) & 0xFFFF; - fixed (byte* bufferPtr = buffer) + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) { byte* localBufferPtr = bufferPtr; uint length = (uint)buffer.Length; diff --git a/src/ImageSharp/Compression/Zlib/Crc32.cs b/src/ImageSharp/Compression/Zlib/Crc32.cs index 2d0a09bd4c..e21621ab74 100644 --- a/src/ImageSharp/Compression/Zlib/Crc32.cs +++ b/src/ImageSharp/Compression/Zlib/Crc32.cs @@ -81,7 +81,7 @@ private static unsafe uint CalculateSse(uint crc, ReadOnlySpan buffer) int chunksize = buffer.Length & ~ChunksizeMask; int length = chunksize; - fixed (byte* bufferPtr = buffer) + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) { fixed (ulong* k05PolyPtr = K05Poly) { @@ -201,7 +201,7 @@ private static unsafe uint CalculateSse(uint crc, ReadOnlySpan buffer) [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] private static unsafe uint CalculateArm(uint crc, ReadOnlySpan buffer) { - fixed (byte* bufferPtr = buffer) + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) { byte* localBufferPtr = bufferPtr; int len = buffer.Length; @@ -248,7 +248,7 @@ private static unsafe uint CalculateArm(uint crc, ReadOnlySpan buffer) [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] private static unsafe uint CalculateArm64(uint crc, ReadOnlySpan buffer) { - fixed (byte* bufferPtr = buffer) + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) { byte* localBufferPtr = bufferPtr; int len = buffer.Length; diff --git a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs index 8d64a09dfb..ab6e4cfccf 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs @@ -569,7 +569,7 @@ public static void PredictorInverseTransform( Span pixelData, Span outputSpan) { - fixed (uint* inputFixed = pixelData) + fixed (uint* inputFixed = &MemoryMarshal.GetReference(pixelData)) { fixed (uint* outputFixed = outputSpan) { @@ -1474,8 +1474,7 @@ private static uint Select(uint a, uint b, uint c, Span scratch) { if (Sse2.IsSupported) { - Span output = scratch; - fixed (short* p = output) + fixed (short* ptr = &MemoryMarshal.GetReference(scratch)) { Vector128 a0 = Sse2.ConvertScalarToVector128UInt32(a).AsByte(); Vector128 b0 = Sse2.ConvertScalarToVector128UInt32(b).AsByte(); @@ -1489,8 +1488,8 @@ private static uint Select(uint a, uint b, uint c, Span scratch) Vector128 pa = Sse2.UnpackLow(ac, Vector128.Zero); // |a - c| Vector128 pb = Sse2.UnpackLow(bc, Vector128.Zero); // |b - c| Vector128 diff = Sse2.Subtract(pb.AsUInt16(), pa.AsUInt16()); - Sse2.Store((ushort*)p, diff); - int paMinusPb = output[3] + output[2] + output[1] + output[0]; + Sse2.Store((ushort*)ptr, diff); + int paMinusPb = ptr[3] + ptr[2] + ptr[1] + ptr[0]; return (paMinusPb <= 0) ? a : b; } } diff --git a/src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs index 689c63f5b1..4113579637 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs @@ -353,8 +353,8 @@ private static void GetResidual( else { #pragma warning disable SA1503 // Braces should not be omitted - fixed (uint* currentRow = currentRowSpan) - fixed (uint* upperRow = upperRowSpan) + fixed (uint* currentRow = &MemoryMarshal.GetReference(currentRowSpan)) + fixed (uint* upperRow = &MemoryMarshal.GetReference(upperRowSpan)) { for (int x = xStart; x < xEnd; x++) { @@ -664,9 +664,9 @@ private static void PredictBatch( Span scratch) { #pragma warning disable SA1503 // Braces should not be omitted - fixed (uint* current = currentSpan) - fixed (uint* upper = upperSpan) - fixed (uint* outputFixed = outputSpan) + fixed (uint* current = &MemoryMarshal.GetReference(currentSpan)) + fixed (uint* upper = &MemoryMarshal.GetReference(upperSpan)) + fixed (uint* outputFixed = &MemoryMarshal.GetReference(outputSpan)) { uint* output = outputFixed; if (xStart == 0) diff --git a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs index 1a8fcbafc9..735d0bf557 100644 --- a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs +++ b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs @@ -25,7 +25,7 @@ public static unsafe bool CheckNonOpaque(Span row) ReadOnlySpan rowBytes = MemoryMarshal.AsBytes(row); int i = 0; int length = (row.Length * 4) - 3; - fixed (byte* src = rowBytes) + fixed (byte* src = &MemoryMarshal.GetReference(rowBytes)) { var alphaMaskVector256 = Vector256.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255); Vector256 all0x80Vector256 = Vector256.Create((byte)0x80).AsByte(); @@ -81,7 +81,7 @@ public static unsafe bool CheckNonOpaque(Span row) ReadOnlySpan rowBytes = MemoryMarshal.AsBytes(row); int i = 0; int length = (row.Length * 4) - 3; - fixed (byte* src = rowBytes) + fixed (byte* src = &MemoryMarshal.GetReference(rowBytes)) { for (; i + 64 <= length; i += 64) { diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 2eb05ea935..31617c163b 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -50,7 +50,7 @@ internal static unsafe void DangerousCopyColumns( Span span = MemoryMarshal.AsBytes(buffer.DangerousGetSingleMemory().Span); - fixed (byte* ptr = span) + fixed (byte* ptr = &MemoryMarshal.GetReference(span)) { byte* basePtr = ptr; for (int y = 0; y < buffer.Height; y++) From 45321948c837b675b6339dafaf7ac8d7fc5e9421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Sun, 26 Mar 2023 14:19:26 +0200 Subject: [PATCH 2/6] Block8x8 set explicit size instead of (unused) fixed sized buffer field --- .../Formats/Jpeg/Components/Block8x8.cs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs index d119a18c8b..b417a8c814 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs @@ -15,25 +15,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components; /// 8x8 matrix of coefficients. /// // ReSharper disable once InconsistentNaming -[StructLayout(LayoutKind.Explicit)] -internal unsafe partial struct Block8x8 +[StructLayout(LayoutKind.Explicit, Size = 2 * Size)] +internal partial struct Block8x8 { /// /// A number of scalar coefficients in a /// public const int Size = 64; -#pragma warning disable IDE0051 // Remove unused private member - /// - /// A placeholder buffer so the actual struct occupies exactly 64 * 2 bytes. - /// - /// - /// This is not used directly in the code. - /// - [FieldOffset(0)] - private fixed short data[Size]; -#pragma warning restore IDE0051 - /// /// Gets or sets a value at the given index /// From a534328dc482683834c6901d93da61cf0d053724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Sun, 26 Mar 2023 14:34:07 +0200 Subject: [PATCH 3/6] Optimized Block8x8F --- .../Formats/Jpeg/Components/Block8x8F.cs | 25 +++++++------------ .../Block8x8F_MultiplyInPlaceBlock.cs | 4 +-- .../Formats/Jpg/Block8x8FTests.cs | 12 +++------ .../ImageSharp.Tests/Formats/Jpg/DCTTests.cs | 15 ++++------- ...ferenceImplementationsTests.AccurateDCT.cs | 3 +-- ...plementationsTests.FastFloatingPointDCT.cs | 3 +-- ...ceImplementations.LLM_FloatingPoint_DCT.cs | 6 ++--- 7 files changed, 23 insertions(+), 45 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index 21971a8c7d..d432e82d24 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -101,24 +101,17 @@ internal float this[nuint idx] set => this[((uint)y * 8) + (uint)x] = value; } - public static Block8x8F Load(Span data) - { - Block8x8F result = default; - result.LoadFrom(data); - return result; - } - /// /// Load raw 32bit floating point data from source. /// - /// Source + /// Source [MethodImpl(InliningOptions.ShortMethod)] - public void LoadFrom(Span source) + public static Block8x8F Load(Span data) { - ref byte s = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref byte d = ref Unsafe.As(ref this); + DebugGuard.MustBeGreaterThanOrEqualTo(data.Length, Size, "data is too small"); - Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float)); + ref byte src = ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + return Unsafe.ReadUnaligned(ref src); } /// @@ -144,10 +137,10 @@ public unsafe void LoadFrom(Span source) [MethodImpl(InliningOptions.ShortMethod)] public unsafe void ScaledCopyTo(float[] dest) { - fixed (void* ptr = &this.V0L) - { - Marshal.Copy((IntPtr)ptr, dest, 0, Size); - } + DebugGuard.MustBeGreaterThanOrEqualTo(dest.Length, Size, "dest is too small"); + + ref byte destRef = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(dest)); + Unsafe.WriteUnaligned(ref destRef, this); } public float[] ToArray() diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs index a5abeb3b66..722b095870 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_MultiplyInPlaceBlock.cs @@ -29,8 +29,6 @@ private static Block8x8F Create8x8FloatData() } } - var source = default(Block8x8F); - source.LoadFrom(result); - return source; + return Block8x8F.Load(result); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 3e4bcae6b7..cde9e776b2 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -99,8 +99,7 @@ public void Load_Store_FloatArray() Times, () => { - var b = default(Block8x8F); - b.LoadFrom(data); + Block8x8F b = Block8x8F.Load(data); b.ScaledCopyTo(mirror); }); @@ -117,8 +116,7 @@ static void RunTest() float[] expected = Create8x8FloatData(); ReferenceImplementations.Transpose8x8(expected); - var block8x8 = default(Block8x8F); - block8x8.LoadFrom(Create8x8FloatData()); + Block8x8F block8x8 = Block8x8F.Load(Create8x8FloatData()); block8x8.TransposeInplace(); @@ -153,9 +151,8 @@ private static float[] Create8x8ColorCropTestData() [Fact] public void NormalizeColors() { - var block = default(Block8x8F); float[] input = Create8x8ColorCropTestData(); - block.LoadFrom(input); + Block8x8F block = Block8x8F.Load(input); this.Output.WriteLine("Input:"); this.PrintLinearData(input); @@ -242,8 +239,7 @@ public void RoundInto() { float[] data = Create8x8RandomFloatData(-1000, 1000); - var source = default(Block8x8F); - source.LoadFrom(data); + Block8x8F source = Block8x8F.Load(data); var dest = default(Block8x8); source.RoundInto(ref dest); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs index 5853ff37a5..5a1488c411 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs @@ -114,8 +114,7 @@ static void RunTest(string serialized) int seed = FeatureTestRunner.Deserialize(serialized); Span src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed); - var srcBlock = default(Block8x8F); - srcBlock.LoadFrom(src); + Block8x8F srcBlock = Block8x8F.Load(src); float[] expectedDest = new float[64]; float[] temp = new float[64]; @@ -162,8 +161,7 @@ static void RunTest(string serialized) public void TranformIDCT_4x4(int seed) { Span src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed, 4, 4); - var srcBlock = default(Block8x8F); - srcBlock.LoadFrom(src); + Block8x8F srcBlock = Block8x8F.Load(src); float[] expectedDest = new float[64]; float[] temp = new float[64]; @@ -224,8 +222,7 @@ static void AssertScaledElementEquality(Span expected, Span actual public void TranformIDCT_2x2(int seed) { Span src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed, 2, 2); - var srcBlock = default(Block8x8F); - srcBlock.LoadFrom(src); + Block8x8F srcBlock = Block8x8F.Load(src); float[] expectedDest = new float[64]; float[] temp = new float[64]; @@ -286,8 +283,7 @@ static void AssertScaledElementEquality(Span expected, Span actual public void TranformIDCT_1x1(int seed) { Span src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed, 1, 1); - var srcBlock = default(Block8x8F); - srcBlock.LoadFrom(src); + Block8x8F srcBlock = Block8x8F.Load(src); float[] expectedDest = new float[64]; float[] temp = new float[64]; @@ -330,8 +326,7 @@ static void RunTest(string serialized) int seed = FeatureTestRunner.Deserialize(serialized); Span src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed); - var block = default(Block8x8F); - block.LoadFrom(src); + Block8x8F block = Block8x8F.Load(src); float[] expectedDest = new float[64]; float[] temp1 = new float[64]; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs index cd93adefd8..c593a029ab 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs @@ -25,8 +25,7 @@ public void ForwardThenInverse(int seed) { float[] data = Create8x8RandomFloatData(-1000, 1000, seed); - var b0 = default(Block8x8F); - b0.LoadFrom(data); + Block8x8F b0 = Block8x8F.Load(data); Block8x8F b1 = ReferenceImplementations.AccurateDCT.TransformFDCT(ref b0); Block8x8F b2 = ReferenceImplementations.AccurateDCT.TransformIDCT(ref b1); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs index 46c6ee2da6..f5d7c159ba 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs @@ -70,8 +70,7 @@ public void LLM_FDCT_IsEquivalentTo_AccurateImplementation(int seed) { float[] floatData = Create8x8RandomFloatData(-1000, 1000); - Block8x8F source = default; - source.LoadFrom(floatData); + Block8x8F source = Block8x8F.Load(floatData); Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); Block8x8F actual = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformFDCT_UpscaleBy8(ref source); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs index 9e0c62d139..0d5f3114d1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs @@ -36,8 +36,7 @@ public static Block8x8F TransformIDCT(ref Block8x8F source) float[] temp = new float[64]; IDCT2D_llm(s, d, temp); - Block8x8F result = default; - result.LoadFrom(d); + Block8x8F result = Block8x8F.Load(d); return result; } @@ -49,8 +48,7 @@ public static Block8x8F TransformFDCT_UpscaleBy8(ref Block8x8F source) float[] temp = new float[64]; FDCT2D_llm(s, d, temp); - Block8x8F result = default; - result.LoadFrom(d); + Block8x8F result = Block8x8F.Load(d); return result; } From f348d704efac5bcb001cd8ba40b12d1cc7329d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Mon, 27 Mar 2023 18:34:02 +0200 Subject: [PATCH 4/6] Block8x8 Load and CopyTo simplified Similar to a534328dc482683834c6901d93da61cf0d053724 --- .../Formats/Jpeg/Components/Block8x8.cs | 27 ++++++------------- .../Formats/Jpg/Block8x8Tests.cs | 3 +-- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs index b417a8c814..01d112bd6f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs @@ -63,9 +63,10 @@ public short this[int idx] public static Block8x8 Load(Span data) { - Unsafe.SkipInit(out Block8x8 result); - result.LoadFrom(data); - return result; + DebugGuard.MustBeGreaterThanOrEqualTo(data.Length, Size, "data is too small"); + + ref byte src = ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + return Unsafe.ReadUnaligned(ref src); } /// @@ -93,9 +94,10 @@ public short[] ToArray() /// public void CopyTo(Span destination) { - ref byte selfRef = ref Unsafe.As(ref this); - ref byte destRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(destination)); - Unsafe.CopyBlockUnaligned(ref destRef, ref selfRef, Size * sizeof(short)); + DebugGuard.MustBeGreaterThanOrEqualTo(destination.Length, Size, "destination is too small"); + + ref byte destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); + Unsafe.WriteUnaligned(ref destRef, this); } /// @@ -124,19 +126,6 @@ public void LoadFrom(ReadOnlySpan source) } } - /// - /// Load raw 16bit integers from source. - /// - /// Source - [MethodImpl(InliningOptions.ShortMethod)] - public void LoadFrom(Span source) - { - ref byte sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); - ref byte destRef = ref Unsafe.As(ref this); - - Unsafe.CopyBlockUnaligned(ref destRef, ref sourceRef, Size * sizeof(short)); - } - /// /// Cast and copy -s from the beginning of 'source' span. /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs index 798ea30407..b5d364dd38 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs @@ -269,8 +269,7 @@ static void RunTest() short[] expected = Create8x8ShortData(); ReferenceImplementations.Transpose8x8(expected); - var block8x8 = default(Block8x8); - block8x8.LoadFrom(Create8x8ShortData()); + Block8x8 block8x8 = Block8x8.Load(Create8x8ShortData()); block8x8.TransposeInplace(); From 4b84aecb94d37e934051273e2487ebe798424c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Mon, 27 Mar 2023 19:09:25 +0200 Subject: [PATCH 5/6] Reverted some pinning changes to have less verbose C# code --- .../Common/Helpers/SimdUtils.HwIntrinsics.cs | 2 +- src/ImageSharp/Compression/Zlib/Adler32.cs | 2 +- src/ImageSharp/Compression/Zlib/Crc32.cs | 6 +++--- src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs | 2 +- .../Formats/Webp/Lossless/PredictorEncoder.cs | 10 +++++----- src/ImageSharp/Formats/Webp/WebpCommonUtils.cs | 2 +- src/ImageSharp/Memory/Buffer2DExtensions.cs | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs index fd8a61350f..8ba1b09125 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs @@ -673,7 +673,7 @@ internal static unsafe void ByteToNormalizedFloat( ReadOnlySpan source, Span dest) { - fixed (byte* sourceBase = &MemoryMarshal.GetReference(source)) + fixed (byte* sourceBase = source) { if (Avx2.IsSupported) { diff --git a/src/ImageSharp/Compression/Zlib/Adler32.cs b/src/ImageSharp/Compression/Zlib/Adler32.cs index 3ecdf81539..dd8217541a 100644 --- a/src/ImageSharp/Compression/Zlib/Adler32.cs +++ b/src/ImageSharp/Compression/Zlib/Adler32.cs @@ -387,7 +387,7 @@ private static unsafe uint CalculateScalar(uint adler, ReadOnlySpan buffer uint s1 = adler & 0xFFFF; uint s2 = (adler >> 16) & 0xFFFF; - fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) + fixed (byte* bufferPtr = buffer) { byte* localBufferPtr = bufferPtr; uint length = (uint)buffer.Length; diff --git a/src/ImageSharp/Compression/Zlib/Crc32.cs b/src/ImageSharp/Compression/Zlib/Crc32.cs index e21621ab74..2d0a09bd4c 100644 --- a/src/ImageSharp/Compression/Zlib/Crc32.cs +++ b/src/ImageSharp/Compression/Zlib/Crc32.cs @@ -81,7 +81,7 @@ private static unsafe uint CalculateSse(uint crc, ReadOnlySpan buffer) int chunksize = buffer.Length & ~ChunksizeMask; int length = chunksize; - fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) + fixed (byte* bufferPtr = buffer) { fixed (ulong* k05PolyPtr = K05Poly) { @@ -201,7 +201,7 @@ private static unsafe uint CalculateSse(uint crc, ReadOnlySpan buffer) [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] private static unsafe uint CalculateArm(uint crc, ReadOnlySpan buffer) { - fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) + fixed (byte* bufferPtr = buffer) { byte* localBufferPtr = bufferPtr; int len = buffer.Length; @@ -248,7 +248,7 @@ private static unsafe uint CalculateArm(uint crc, ReadOnlySpan buffer) [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] private static unsafe uint CalculateArm64(uint crc, ReadOnlySpan buffer) { - fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) + fixed (byte* bufferPtr = buffer) { byte* localBufferPtr = bufferPtr; int len = buffer.Length; diff --git a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs index ab6e4cfccf..024adb7c23 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs @@ -569,7 +569,7 @@ public static void PredictorInverseTransform( Span pixelData, Span outputSpan) { - fixed (uint* inputFixed = &MemoryMarshal.GetReference(pixelData)) + fixed (uint* inputFixed = pixelData) { fixed (uint* outputFixed = outputSpan) { diff --git a/src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs index 4113579637..689c63f5b1 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/PredictorEncoder.cs @@ -353,8 +353,8 @@ private static void GetResidual( else { #pragma warning disable SA1503 // Braces should not be omitted - fixed (uint* currentRow = &MemoryMarshal.GetReference(currentRowSpan)) - fixed (uint* upperRow = &MemoryMarshal.GetReference(upperRowSpan)) + fixed (uint* currentRow = currentRowSpan) + fixed (uint* upperRow = upperRowSpan) { for (int x = xStart; x < xEnd; x++) { @@ -664,9 +664,9 @@ private static void PredictBatch( Span scratch) { #pragma warning disable SA1503 // Braces should not be omitted - fixed (uint* current = &MemoryMarshal.GetReference(currentSpan)) - fixed (uint* upper = &MemoryMarshal.GetReference(upperSpan)) - fixed (uint* outputFixed = &MemoryMarshal.GetReference(outputSpan)) + fixed (uint* current = currentSpan) + fixed (uint* upper = upperSpan) + fixed (uint* outputFixed = outputSpan) { uint* output = outputFixed; if (xStart == 0) diff --git a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs index 735d0bf557..4a7dc74ca7 100644 --- a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs +++ b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs @@ -81,7 +81,7 @@ public static unsafe bool CheckNonOpaque(Span row) ReadOnlySpan rowBytes = MemoryMarshal.AsBytes(row); int i = 0; int length = (row.Length * 4) - 3; - fixed (byte* src = &MemoryMarshal.GetReference(rowBytes)) + fixed (byte* src = rowBytes) { for (; i + 64 <= length; i += 64) { diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 31617c163b..2eb05ea935 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -50,7 +50,7 @@ internal static unsafe void DangerousCopyColumns( Span span = MemoryMarshal.AsBytes(buffer.DangerousGetSingleMemory().Span); - fixed (byte* ptr = &MemoryMarshal.GetReference(span)) + fixed (byte* ptr = span) { byte* basePtr = ptr; for (int y = 0; y < buffer.Height; y++) From 66b2f327bed811d7abce65921148d797ee4fdbd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Mon, 27 Mar 2023 19:19:02 +0200 Subject: [PATCH 6/6] Reverted Webp/WebpCommonUtils fixed-verbosity --- src/ImageSharp/Formats/Webp/WebpCommonUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs index 4a7dc74ca7..1a8fcbafc9 100644 --- a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs +++ b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs @@ -25,7 +25,7 @@ public static unsafe bool CheckNonOpaque(Span row) ReadOnlySpan rowBytes = MemoryMarshal.AsBytes(row); int i = 0; int length = (row.Length * 4) - 3; - fixed (byte* src = &MemoryMarshal.GetReference(rowBytes)) + fixed (byte* src = rowBytes) { var alphaMaskVector256 = Vector256.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255); Vector256 all0x80Vector256 = Vector256.Create((byte)0x80).AsByte();