From 8bd19cb5642aa298cfaefa3674c3125e6d5be52f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 4 Feb 2020 02:53:04 +0100 Subject: [PATCH] Improve robustness of discontiguous Buffer2D --- .../Advanced/AdvancedImageExtensions.cs | 4 +- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 6 +- src/ImageSharp/Formats/Tga/TgaEncoderCore.cs | 2 +- .../ImageFrameCollection{TPixel}.cs | 2 +- src/ImageSharp/ImageFrame{TPixel}.cs | 8 +-- src/ImageSharp/Memory/Buffer2DExtensions.cs | 62 ++++++++----------- src/ImageSharp/Memory/Buffer2D{T}.cs | 45 +++++++++++++- src/ImageSharp/Memory/BufferArea{T}.cs | 8 +-- .../DiscontiguousBuffers/MemoryGroup{T}.cs | 10 ++- .../Transforms/Resize/ResizeKernelMap.cs | 2 +- .../Transforms/Resize/ResizeWorker.cs | 4 +- .../Formats/Jpg/SpectralJpegTests.cs | 2 +- .../Helpers/ParallelHelperTests.cs | 2 +- .../Helpers/RowIntervalTests.cs | 2 +- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 34 ++++++++-- .../Memory/BufferAreaTests.cs | 2 +- .../MemoryGroupTests.Allocate.cs | 12 +++- .../TestUtilities/TestImageExtensions.cs | 2 +- 18 files changed, 140 insertions(+), 69 deletions(-) diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index cfaffbbb2..79a863ff4 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Advanced internal static Memory GetPixelMemory(this ImageFrame source) where TPixel : struct, IPixel { - return source.PixelBuffer.GetMemory(); + return source.PixelBuffer.GetSingleMemory(); } /// @@ -185,6 +185,6 @@ namespace SixLabors.ImageSharp.Advanced /// A reference to the element. private static ref TPixel DangerousGetPinnableReferenceToPixelBuffer(IPixelSource source) where TPixel : struct, IPixel - => ref MemoryMarshal.GetReference(source.PixelBuffer.GetSpan()); + => ref MemoryMarshal.GetReference(source.PixelBuffer.GetSingleSpan()); } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 8d82d28fb..3e1637e70 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -301,11 +301,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp Span rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span; if (compression == BmpCompression.RLE8) { - this.UncompressRle8(width, buffer.GetSpan(), undefinedPixels.GetSpan(), rowsWithUndefinedPixelsSpan); + this.UncompressRle8(width, buffer.GetSingleSpan(), undefinedPixels.GetSingleSpan(), rowsWithUndefinedPixelsSpan); } else { - this.UncompressRle4(width, buffer.GetSpan(), undefinedPixels.GetSpan(), rowsWithUndefinedPixelsSpan); + this.UncompressRle4(width, buffer.GetSingleSpan(), undefinedPixels.GetSingleSpan(), rowsWithUndefinedPixelsSpan); } for (int y = 0; y < height; y++) @@ -377,7 +377,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { Span rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span; Span bufferSpan = buffer.GetSpan(); - this.UncompressRle24(width, bufferSpan, undefinedPixels.GetSpan(), rowsWithUndefinedPixelsSpan); + this.UncompressRle24(width, bufferSpan, undefinedPixels.GetSingleSpan(), rowsWithUndefinedPixelsSpan); for (int y = 0; y < height; y++) { int newY = Invert(y, height, inverted); diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs index a4b141f38..1306061c5 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs @@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Formats.Tga { Rgba32 color = default; Buffer2D pixels = image.PixelBuffer; - Span pixelSpan = pixels.GetSpan(); + Span pixelSpan = pixels.GetSingleSpan(); int totalPixels = image.Width * image.Height; int encodedPixels = 0; while (encodedPixels < totalPixels) diff --git a/src/ImageSharp/ImageFrameCollection{TPixel}.cs b/src/ImageSharp/ImageFrameCollection{TPixel}.cs index b11c74958..635ed5b77 100644 --- a/src/ImageSharp/ImageFrameCollection{TPixel}.cs +++ b/src/ImageSharp/ImageFrameCollection{TPixel}.cs @@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp this.parent.GetConfiguration(), source.Size(), source.Metadata.DeepClone()); - source.CopyPixelsTo(result.PixelBuffer.GetSpan()); + source.CopyPixelsTo(result.PixelBuffer.GetSingleSpan()); return result; } } diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 421cd1032..0d69b7666 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp Guard.NotNull(source, nameof(source)); this.PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D(source.PixelBuffer.Width, source.PixelBuffer.Height); - source.PixelBuffer.GetSpan().CopyTo(this.PixelBuffer.GetSpan()); + source.PixelBuffer.GetSingleSpan().CopyTo(this.PixelBuffer.GetSingleSpan()); } /// @@ -179,7 +179,7 @@ namespace SixLabors.ImageSharp throw new ArgumentException("ImageFrame.CopyTo(): target must be of the same size!", nameof(target)); } - this.GetPixelSpan().CopyTo(target.GetSpan()); + this.GetPixelSpan().CopyTo(target.GetSingleSpan()); } /// @@ -216,10 +216,10 @@ namespace SixLabors.ImageSharp if (typeof(TPixel) == typeof(TDestinationPixel)) { Span dest1 = MemoryMarshal.Cast(destination); - this.PixelBuffer.GetSpan().CopyTo(dest1); + this.PixelBuffer.GetSingleSpan().CopyTo(dest1); } - PixelOperations.Instance.To(this.GetConfiguration(), this.PixelBuffer.GetSpan(), destination); + PixelOperations.Instance.To(this.GetConfiguration(), this.PixelBuffer.GetSingleSpan(), destination); } /// diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 959ad94bb..829a2767a 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -15,59 +15,49 @@ namespace SixLabors.ImageSharp.Memory public static class Buffer2DExtensions { /// - /// Gets a to the backing buffer of . + /// Gets a to the backing data of + /// if the backing group consists of one single contiguous memory buffer. + /// Throws otherwise. /// /// The . /// The value type. /// The referencing the memory area. - public static Span GetSpan(this Buffer2D buffer) + /// + /// Thrown when the backing group is discontiguous. + /// + public static Span GetSingleSpan(this Buffer2D buffer) where T : struct { Guard.NotNull(buffer, nameof(buffer)); + if (buffer.MemoryGroup.Count > 1) + { + throw new InvalidOperationException("GetSingleSpan is only valid for a single-buffer group!"); + } + return buffer.MemoryGroup.Single().Span; } /// - /// Gets the holding the backing buffer of . + /// Gets a to the backing data of + /// if the backing group consists of one single contiguous memory buffer. + /// Throws otherwise. /// /// The . /// The value type. /// The . - public static Memory GetMemory(this Buffer2D buffer) - where T : struct - { - Guard.NotNull(buffer, nameof(buffer)); - return buffer.MemoryGroup.Single(); - } - - /// - /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. - /// - /// The buffer - /// The y (row) coordinate - /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span GetRowSpan(this Buffer2D buffer, int y) + /// + /// Thrown when the backing group is discontiguous. + /// + public static Memory GetSingleMemory(this Buffer2D buffer) where T : struct { Guard.NotNull(buffer, nameof(buffer)); - return buffer.MemoryGroup.GetBoundedSlice(y * buffer.Width, buffer.Width).Span; - } + if (buffer.MemoryGroup.Count > 1) + { + throw new InvalidOperationException("GetSingleMemory is only valid for a single-buffer group!"); + } - /// - /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. - /// - /// The buffer - /// The y (row) coordinate - /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory GetRowMemory(this Buffer2D buffer, int y) - where T : struct - { - Guard.NotNull(buffer, nameof(buffer)); - return buffer.MemoryGroup.GetBoundedSlice(y * buffer.Width, buffer.Width); + return buffer.MemoryGroup.Single(); } /// @@ -93,7 +83,7 @@ namespace SixLabors.ImageSharp.Memory int dOffset = destIndex * elementSize; long count = columnCount * elementSize; - Span span = MemoryMarshal.AsBytes(buffer.GetMemory().Span); + Span span = MemoryMarshal.AsBytes(buffer.GetSingleMemory().Span); fixed (byte* ptr = span) { @@ -153,7 +143,7 @@ namespace SixLabors.ImageSharp.Memory internal static Span GetMultiRowSpan(this Buffer2D buffer, in RowInterval rows) where T : struct { - return buffer.GetSpan().Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); + return buffer.GetSingleSpan().Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); } /// diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 439a5bde2..ea2568efd 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -17,6 +17,8 @@ namespace SixLabors.ImageSharp.Memory public sealed class Buffer2D : IDisposable where T : struct { + private Memory cachedMemory = default; + /// /// Initializes a new instance of the class. /// @@ -28,6 +30,11 @@ namespace SixLabors.ImageSharp.Memory this.MemoryGroup = memoryGroup; this.Width = width; this.Height = height; + + if (memoryGroup.Count == 1) + { + this.cachedMemory = memoryGroup[0]; + } } /// @@ -63,12 +70,39 @@ namespace SixLabors.ImageSharp.Memory } } + /// + /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. + /// + /// The y (row) coordinate. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetRowSpan(int y) + { + return this.cachedMemory.Length > 0 + ? this.cachedMemory.Span.Slice(y * this.Width, this.Width) + : this.GetRowMemorySlow(y).Span; + } + + /// + /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. + /// + /// The y (row) coordinate. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Memory GetRowMemory(int y) + { + return this.cachedMemory.Length > 0 + ? this.cachedMemory.Slice(y * this.Width, this.Width) + : this.GetRowMemorySlow(y); + } + /// /// Disposes the instance /// public void Dispose() { this.MemoryGroup.Dispose(); + this.cachedMemory = default; } /// @@ -78,10 +112,13 @@ namespace SixLabors.ImageSharp.Memory internal static void SwapOrCopyContent(Buffer2D destination, Buffer2D source) { MemoryGroup.SwapOrCopyContent(destination.MemoryGroup, source.MemoryGroup); - SwapDimensionData(destination, source); + SwapOwnData(destination, source); } - private static void SwapDimensionData(Buffer2D a, Buffer2D b) + [MethodImpl(InliningOptions.ColdPath)] + private Memory GetRowMemorySlow(int y) => this.MemoryGroup.GetBoundedSlice(y * this.Width, this.Width); + + private static void SwapOwnData(Buffer2D a, Buffer2D b) { Size aSize = a.Size(); Size bSize = b.Size(); @@ -91,6 +128,10 @@ namespace SixLabors.ImageSharp.Memory a.Width = bSize.Width; a.Height = bSize.Height; + + Memory aCached = a.cachedMemory; + a.cachedMemory = b.cachedMemory; + b.cachedMemory = aCached; } } } diff --git a/src/ImageSharp/Memory/BufferArea{T}.cs b/src/ImageSharp/Memory/BufferArea{T}.cs index ec7665998..983d6b3a7 100644 --- a/src/ImageSharp/Memory/BufferArea{T}.cs +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Memory /// The position inside a row /// The row index /// The reference to the value - public ref T this[int x, int y] => ref this.DestinationBuffer.GetSpan()[this.GetIndexOf(x, y)]; + public ref T this[int x, int y] => ref this.DestinationBuffer.GetSingleSpan()[this.GetIndexOf(x, y)]; /// /// Gets a reference to the [0,0] element. @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Memory /// The reference to the [0,0] element [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T GetReferenceToOrigin() => - ref this.DestinationBuffer.GetSpan()[(this.Rectangle.Y * this.DestinationBuffer.Width) + this.Rectangle.X]; + ref this.DestinationBuffer.GetSingleSpan()[(this.Rectangle.Y * this.DestinationBuffer.Width) + this.Rectangle.X]; /// /// Gets a span to row 'y' inside this area. @@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Memory int xx = this.Rectangle.X; int width = this.Rectangle.Width; - return this.DestinationBuffer.GetSpan().Slice(yy + xx, width); + return this.DestinationBuffer.GetSingleSpan().Slice(yy + xx, width); } /// @@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp.Memory // Optimization for when the size of the area is the same as the buffer size. if (this.IsFullBufferArea) { - this.DestinationBuffer.GetSpan().Clear(); + this.DestinationBuffer.GetSingleSpan().Clear(); return; } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs index d9c384b55..00bd27b34 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory.Internals; namespace SixLabors.ImageSharp.Memory { @@ -69,15 +70,22 @@ namespace SixLabors.ImageSharp.Memory { Guard.NotNull(allocator, nameof(allocator)); Guard.MustBeGreaterThanOrEqualTo(totalLength, 0, nameof(totalLength)); - Guard.MustBeGreaterThan(bufferAlignment, 0, nameof(bufferAlignment)); + Guard.MustBeGreaterThanOrEqualTo(bufferAlignment, 0, nameof(bufferAlignment)); int blockCapacityInElements = allocator.GetBufferCapacityInBytes() / ElementSize; + if (bufferAlignment > blockCapacityInElements) { throw new InvalidMemoryOperationException( $"The buffer capacity of the provided MemoryAllocator is insufficient for the requested buffer alignment: {bufferAlignment}."); } + if (totalLength == 0) + { + var buffers0 = new IMemoryOwner[1] { allocator.Allocate(0, options) }; + return new Owned(buffers0, 0, 0, true); + } + int numberOfAlignedSegments = blockCapacityInElements / bufferAlignment; int bufferLength = numberOfAlignedSegments * bufferAlignment; if (totalLength > 0 && totalLength < bufferLength) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs index 1b653a92c..06eef76e2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.DestinationLength = destinationLength; this.MaxDiameter = (radius * 2) + 1; this.data = memoryAllocator.Allocate2D(this.MaxDiameter, bufferHeight, AllocationOptions.Clean); - this.pinHandle = this.data.GetMemory().Pin(); + this.pinHandle = this.data.GetSingleMemory().Pin(); this.kernels = new ResizeKernel[destinationLength]; this.tempValues = new double[this.MaxDiameter]; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 4f5faa38e..57663c07d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public void FillDestinationPixels(RowInterval rowInterval, Buffer2D destination) { Span tempColSpan = this.tempColumnBuffer.GetSpan(); - Span transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.GetSpan(); + Span transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.GetSingleSpan(); for (int y = rowInterval.Min; y < rowInterval.Max; y++) { @@ -165,7 +165,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private void CalculateFirstPassValues(RowInterval calculationInterval) { Span tempRowSpan = this.tempRowBuffer.GetSpan(); - Span transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.GetSpan(); + Span transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.GetSingleSpan(); for (int y = calculationInterval.Min; y < calculationInterval.Max; y++) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs index 75e6da5d0..c69740ede 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg this.Output.WriteLine($"Component{i}: {diff}"); averageDifference += diff.average; totalDifference += diff.total; - tolerance += libJpegComponent.SpectralBlocks.GetSpan().Length; + tolerance += libJpegComponent.SpectralBlocks.GetSingleSpan().Length; } averageDifference /= componentCount; diff --git a/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs index 5914aba40..0d3002661 100644 --- a/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs +++ b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs @@ -330,7 +330,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers }); // Assert: - TestImageExtensions.CompareBuffers(expected.GetSpan(), actual.GetSpan()); + TestImageExtensions.CompareBuffers(expected.GetSingleSpan(), actual.GetSingleSpan()); } } diff --git a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs index 0bb3f49d6..222770195 100644 --- a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs +++ b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers Span span = buffer.GetMultiRowSpan(rows); - ref int expected0 = ref buffer.GetSpan()[min * width]; + ref int expected0 = ref buffer.GetSingleSpan()[min * width]; int expectedLength = (max - min) * width; ref int actual0 = ref span[0]; diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index a11602280..03a5f2273 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -50,12 +50,30 @@ namespace SixLabors.ImageSharp.Tests.Memory } } + [Theory] + [InlineData(Big, 0, 42)] + [InlineData(Big, 1, 0)] + [InlineData(60, 42, 0)] + [InlineData(3, 0, 0)] + public unsafe void Construct_Empty(int bufferCapacity, int width, int height) + { + this.MemoryAllocator.BufferCapacityInBytes = sizeof(TestStructs.Foo) * bufferCapacity; + + using (Buffer2D buffer = this.MemoryAllocator.Allocate2D(width, height)) + { + Assert.Equal(width, buffer.Width); + Assert.Equal(height, buffer.Height); + Assert.Equal(0, buffer.MemoryGroup.TotalLength); + Assert.Equal(0, buffer.GetSingleSpan().Length); + } + } + [Fact] public void CreateClean() { using (Buffer2D buffer = this.MemoryAllocator.Allocate2D(42, 42, AllocationOptions.Clean)) { - Span span = buffer.GetSpan(); + Span span = buffer.GetSingleSpan(); for (int j = 0; j < span.Length; j++) { Assert.Equal(0, span[j]); @@ -114,9 +132,12 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void SwapOrCopyContent() { - using (Buffer2D a = this.MemoryAllocator.Allocate2D(10, 5)) - using (Buffer2D b = this.MemoryAllocator.Allocate2D(3, 7)) + using (Buffer2D a = this.MemoryAllocator.Allocate2D(10, 5, AllocationOptions.Clean)) + using (Buffer2D b = this.MemoryAllocator.Allocate2D(3, 7, AllocationOptions.Clean)) { + a[1, 3] = 666; + b[1, 3] = 444; + Memory aa = a.MemoryGroup.Single(); Memory bb = b.MemoryGroup.Single(); @@ -127,6 +148,9 @@ namespace SixLabors.ImageSharp.Tests.Memory Assert.Equal(new Size(3, 7), a.Size()); Assert.Equal(new Size(10, 5), b.Size()); + + Assert.Equal(666, b[1, 3]); + Assert.Equal(444, a[1, 3]); } } @@ -142,7 +166,7 @@ namespace SixLabors.ImageSharp.Tests.Memory var rnd = new Random(123); using (Buffer2D b = this.MemoryAllocator.Allocate2D(width, height)) { - rnd.RandomFill(b.GetSpan(), 0, 1); + rnd.RandomFill(b.GetSingleSpan(), 0, 1); b.CopyColumns(startIndex, destIndex, columnCount); @@ -164,7 +188,7 @@ namespace SixLabors.ImageSharp.Tests.Memory var rnd = new Random(123); using (Buffer2D b = this.MemoryAllocator.Allocate2D(100, 100)) { - rnd.RandomFill(b.GetSpan(), 0, 1); + rnd.RandomFill(b.GetSingleSpan(), 0, 1); b.CopyColumns(0, 50, 22); b.CopyColumns(0, 50, 22); diff --git a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs index 9f523156f..a0112ce90 100644 --- a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs @@ -117,7 +117,7 @@ namespace SixLabors.ImageSharp.Tests.Memory using (Buffer2D buffer = CreateTestBuffer(22, 13)) { buffer.GetArea().Clear(); - Span fullSpan = buffer.GetSpan(); + Span fullSpan = buffer.GetSingleSpan(); Assert.True(fullSpan.SequenceEqual(new int[fullSpan.Length])); } } diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs index 972f6cb26..298b5a93f 100644 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs @@ -21,7 +21,10 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers { default(S5), 22, 4, 7, 2, 4, 3 }, { default(S5), 22, 4, 8, 2, 4, 4 }, { default(S5), 22, 4, 21, 6, 4, 1 }, - { default(S5), 22, 4, 0, 0, 4, -1 }, + + // empty: + { default(S5), 22, 0, 0, 1, -1, 0 }, + { default(S5), 22, 4, 0, 1, -1, 0 }, { default(S4), 50, 12, 12, 1, 12, 12 }, { default(S4), 50, 7, 12, 2, 7, 5 }, @@ -61,7 +64,12 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers // Assert: Assert.Equal(expectedNumberOfBuffers, g.Count); - Assert.Equal(expectedBufferSize, g.BufferLength); + + if (expectedBufferSize >= 0) + { + Assert.Equal(expectedBufferSize, g.BufferLength); + } + if (g.Count == 0) { return; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 70d39024e..8cf53bf85 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -672,7 +672,7 @@ namespace SixLabors.ImageSharp.Tests Span pixels = image.Frames.RootFrame.GetPixelSpan(); - Span bufferSpan = buffer.GetSpan(); + Span bufferSpan = buffer.GetSingleSpan(); for (int i = 0; i < bufferSpan.Length; i++) {