From ebe7b9c51693c223e5bcfec0513f476fc31a931b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 30 Oct 2021 22:28:17 +0200 Subject: [PATCH] Remove Image.DangerousGetRowSpan --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 50 +++++++------ .../Tiff/Writers/TiffBiColorWriter{TPixel}.cs | 26 ++++--- .../Formats/Webp/Lossless/Vp8LEncoder.cs | 3 +- .../Formats/Webp/Lossy/YuvConversion.cs | 11 +-- src/ImageSharp/Image{TPixel}.cs | 20 ------ .../ProcessingExtensions.IntegralImage.cs | 5 +- .../Advanced/AdvancedImageExtensionsTests.cs | 17 ----- .../Formats/Png/PngEncoderTests.cs | 48 +++++++------ .../Formats/WebP/PredictorEncoderTests.cs | 15 ++-- .../ImageSharp.Tests/Image/ImageCloneTests.cs | 63 ++++++++-------- .../Image/ImageTests.WrapMemory.cs | 71 ++++++++++++------- tests/ImageSharp.Tests/Image/ImageTests.cs | 1 - .../Image/ProcessPixelRowsTestBase.cs | 17 +++++ .../Quantization/WuQuantizerTests.cs | 62 +++++++++------- .../BasicTestPatternProvider.cs | 32 +++++---- 15 files changed, 243 insertions(+), 198 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 3f1b256fc..4645f22e3 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -165,20 +165,24 @@ namespace SixLabors.ImageSharp.Formats.Png private static void ClearTransparentPixels(Image image) where TPixel : unmanaged, IPixel { - Rgba32 rgba32 = default; - for (int y = 0; y < image.Height; y++) + image.ProcessPixelRows(accessor => { - Span span = image.DangerousGetRowSpan(y); - for (int x = 0; x < image.Width; x++) + Rgba32 rgba32 = default; + for (int y = 0; y < accessor.Height; y++) { - span[x].ToRgba32(ref rgba32); - - if (rgba32.A == 0) + Span span = accessor.GetRowSpan(y); + for (int x = 0; x < accessor.Width; x++) { - span[x].FromRgba32(Color.Transparent); + span[x].ToRgba32(ref rgba32); + + if (rgba32.A == 0) + { + span[x].FromRgba32(Color.Transparent); + } } } - } + }); + } /// @@ -914,27 +918,31 @@ namespace SixLabors.ImageSharp.Formats.Png using IMemoryOwner filterBuffer = this.memoryAllocator.Allocate(filterLength, AllocationOptions.Clean); using IMemoryOwner attemptBuffer = this.memoryAllocator.Allocate(filterLength, AllocationOptions.Clean); - Span filter = filterBuffer.GetSpan(); - Span attempt = attemptBuffer.GetSpan(); - for (int y = 0; y < this.height; y++) + pixels.ProcessPixelRows(accessor => { - this.CollectAndFilterPixelRow(pixels.DangerousGetRowSpan(y), ref filter, ref attempt, quantized, y); - deflateStream.Write(filter); - this.SwapScanlineBuffers(); - } + Span filter = filterBuffer.GetSpan(); + Span attempt = attemptBuffer.GetSpan(); + for (int y = 0; y < this.height; y++) + { + this.CollectAndFilterPixelRow(accessor.GetRowSpan(y), ref filter, ref attempt, quantized, y); + deflateStream.Write(filter); + this.SwapScanlineBuffers(); + } + }); } /// /// Interlaced encoding the pixels. /// /// The type of the pixel. - /// The pixels. + /// The image. /// The deflate stream. - private void EncodeAdam7Pixels(Image pixels, ZlibDeflateStream deflateStream) + private void EncodeAdam7Pixels(Image image, ZlibDeflateStream deflateStream) where TPixel : unmanaged, IPixel { - int width = pixels.Width; - int height = pixels.Height; + int width = image.Width; + int height = image.Height; + Buffer2D pixelBuffer = image.Frames.RootFrame.PixelBuffer; for (int pass = 0; pass < 7; pass++) { int startRow = Adam7.FirstRow[pass]; @@ -959,7 +967,7 @@ namespace SixLabors.ImageSharp.Formats.Png for (int row = startRow; row < height; row += Adam7.RowIncrement[pass]) { // Collect pixel data - Span srcRow = pixels.DangerousGetRowSpan(row); + Span srcRow = pixelBuffer.DangerousGetRowSpan(row); for (int col = startCol, i = 0; col < width; col += Adam7.ColumnIncrement[pass]) { block[i++] = srcRow[col]; diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs index a26fefe15..5fec09ef1 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs @@ -42,18 +42,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers // Special case for T4BitCompressor. int stripPixels = width * height; this.pixelsAsGray ??= this.MemoryAllocator.Allocate(stripPixels); - Span pixelAsGraySpan = this.pixelsAsGray.GetSpan(); - int lastRow = y + height; - int grayRowIdx = 0; - for (int row = y; row < lastRow; row++) + this.imageBlackWhite.ProcessPixelRows(accessor => { - Span pixelsBlackWhiteRow = this.imageBlackWhite.DangerousGetRowSpan(row); - Span pixelAsGrayRow = pixelAsGraySpan.Slice(grayRowIdx * width, width); - PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhiteRow, pixelAsGrayRow, width); - grayRowIdx++; - } + Span pixelAsGraySpan = this.pixelsAsGray.GetSpan(); + int lastRow = y + height; + int grayRowIdx = 0; + for (int row = y; row < lastRow; row++) + { + Span pixelsBlackWhiteRow = accessor.GetRowSpan(row); + Span pixelAsGrayRow = pixelAsGraySpan.Slice(grayRowIdx * width, width); + PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhiteRow, pixelAsGrayRow, width); + grayRowIdx++; + } - compressor.CompressStrip(pixelAsGraySpan.Slice(0, stripPixels), height); + compressor.CompressStrip(pixelAsGraySpan.Slice(0, stripPixels), height); + }); } else { @@ -65,6 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers Span rows = this.bitStrip.Slice(0, bytesPerStrip); rows.Clear(); + Buffer2D blackWhiteBuffer = this.imageBlackWhite.Frames.RootFrame.PixelBuffer; int outputRowIdx = 0; int lastRow = y + height; @@ -73,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers int bitIndex = 0; int byteIndex = 0; Span outputRow = rows.Slice(outputRowIdx * this.BytesPerRow); - Span pixelsBlackWhiteRow = this.imageBlackWhite.DangerousGetRowSpan(row); + Span pixelsBlackWhiteRow = blackWhiteBuffer.DangerousGetRowSpan(row); PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhiteRow, pixelAsGraySpan, width); for (int x = 0; x < this.Image.Width; x++) { diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index 4cdcb3e86..5e5443a2b 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -389,13 +389,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless private bool ConvertPixelsToBgra(Image image, int width, int height) where TPixel : unmanaged, IPixel { + Buffer2D imageBuffer = image.Frames.RootFrame.PixelBuffer; bool nonOpaque = false; Span bgra = this.Bgra.GetSpan(); Span bgraBytes = MemoryMarshal.Cast(bgra); int widthBytes = width * 4; for (int y = 0; y < height; y++) { - Span rowSpan = image.DangerousGetRowSpan(y); + Span rowSpan = imageBuffer.DangerousGetRowSpan(y); Span rowBytes = bgraBytes.Slice(y * widthBytes, widthBytes); PixelOperations.Instance.ToBgra32Bytes(this.configuration, rowSpan, rowBytes, width); if (!nonOpaque) diff --git a/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs b/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs index 254107682..adf39ee2f 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs @@ -31,8 +31,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy public static void ConvertRgbToYuv(Image image, Configuration configuration, MemoryAllocator memoryAllocator, Span y, Span u, Span v) where TPixel : unmanaged, IPixel { - int width = image.Width; - int height = image.Height; + Buffer2D imageBuffer = image.Frames.RootFrame.PixelBuffer; + int width = imageBuffer.Width; + int height = imageBuffer.Height; int uvWidth = (width + 1) >> 1; // Temporary storage for accumulated R/G/B values during conversion to U/V. @@ -46,8 +47,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy int rowIndex; for (rowIndex = 0; rowIndex < height - 1; rowIndex += 2) { - Span rowSpan = image.DangerousGetRowSpan(rowIndex); - Span nextRowSpan = image.DangerousGetRowSpan(rowIndex + 1); + Span rowSpan = imageBuffer.DangerousGetRowSpan(rowIndex); + Span nextRowSpan = imageBuffer.DangerousGetRowSpan(rowIndex + 1); PixelOperations.Instance.ToBgra32(configuration, rowSpan, bgraRow0); PixelOperations.Instance.ToBgra32(configuration, nextRowSpan, bgraRow1); @@ -73,7 +74,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy // Extra last row. if ((height & 1) != 0) { - Span rowSpan = image.DangerousGetRowSpan(rowIndex); + Span rowSpan = imageBuffer.DangerousGetRowSpan(rowIndex); PixelOperations.Instance.ToBgra32(configuration, rowSpan, bgraRow0); ConvertRgbaToY(bgraRow0, y.Slice(rowIndex * width), width); diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index aa3281fbf..710b4fedf 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -300,26 +300,6 @@ namespace SixLabors.ImageSharp } } - /// - /// Gets the representation of the pixels as a of contiguous memory - /// at row beginning from the first pixel on that row. - /// - /// WARNING: Disposing or leaking the underlying image while still working with it's - /// might lead to memory corruption. - /// - /// The row. - /// The - /// Thrown when row index is out of range. - public Span DangerousGetRowSpan(int rowIndex) - { - Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex)); - Guard.MustBeLessThan(rowIndex, this.Height, nameof(rowIndex)); - - this.EnsureNotDisposed(); - - return this.PixelSourceUnsafe.PixelBuffer.DangerousGetRowSpan(rowIndex); - } - /// /// Gets the representation of the pixels as a in the source image's pixel format /// stored in row major order, if the backing buffer is contiguous. diff --git a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs index fc6aa83a5..a336cfec3 100644 --- a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs +++ b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs @@ -30,11 +30,12 @@ namespace SixLabors.ImageSharp.Processing Buffer2D intImage = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height); ulong sumX0 = 0; + Buffer2D sourceBuffer = source.Frames.RootFrame.PixelBuffer; using (IMemoryOwner tempRow = configuration.MemoryAllocator.Allocate(source.Width)) { Span tempSpan = tempRow.GetSpan(); - Span sourceRow = source.DangerousGetRowSpan(0); + Span sourceRow = sourceBuffer.DangerousGetRowSpan(0); Span destRow = intImage.DangerousGetRowSpan(0); PixelOperations.Instance.ToL8(configuration, sourceRow, tempSpan); @@ -51,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing // All other rows for (int y = 1; y < endY; y++) { - sourceRow = source.DangerousGetRowSpan(y); + sourceRow = sourceBuffer.DangerousGetRowSpan(y); destRow = intImage.DangerousGetRowSpan(y); PixelOperations.Instance.ToL8(configuration, sourceRow, tempSpan); diff --git a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs index fe64811fb..2da0cbf83 100644 --- a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs +++ b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs @@ -148,22 +148,5 @@ namespace SixLabors.ImageSharp.Tests.Advanced Assert.ThrowsAny(() => _ = memory3.Span); Assert.ThrowsAny(() => _ = memory10.Span); } - - [Theory] - [WithBlankImages(1, 1, PixelTypes.Rgba32)] - [WithBlankImages(100, 111, PixelTypes.Rgba32)] - [WithBlankImages(400, 600, PixelTypes.Rgba32)] - public void DangerousGetRowSpan_ShouldReferenceSpanOfMemory(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - provider.LimitAllocatorBufferCapacity().InPixelsSqrt(200); - - using Image image = provider.GetImage(); - - Memory memory = image.DangerousGetPixelRowMemory(image.Height - 1); - Span span = image.DangerousGetRowSpan(image.Height - 1); - - Assert.True(span == memory.Span); - } } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 60c796188..9e99dded8 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -411,21 +411,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png ColorType = colorType }; Rgba32 rgba32 = Color.Blue; - for (int y = 0; y < image.Height; y++) + image.ProcessPixelRows(accessor => { - System.Span rowSpan = image.DangerousGetRowSpan(y); - - // Half of the test image should be transparent. - if (y > 25) + for (int y = 0; y < image.Height; y++) { - rgba32.A = 0; - } + System.Span rowSpan = accessor.GetRowSpan(y); - for (int x = 0; x < image.Width; x++) - { - rowSpan[x].FromRgba32(rgba32); + // Half of the test image should be transparent. + if (y > 25) + { + rgba32.A = 0; + } + + for (int x = 0; x < image.Width; x++) + { + rowSpan[x].FromRgba32(rgba32); + } } - } + }); // act using var memStream = new MemoryStream(); @@ -441,20 +444,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png expectedColor = new Rgba32(luminance, luminance, luminance); } - for (int y = 0; y < actual.Height; y++) + actual.ProcessPixelRows(accessor => { - System.Span rowSpan = actual.DangerousGetRowSpan(y); - - if (y > 25) + for (int y = 0; y < accessor.Height; y++) { - expectedColor = Color.Transparent; - } + System.Span rowSpan = accessor.GetRowSpan(y); - for (int x = 0; x < actual.Width; x++) - { - Assert.Equal(expectedColor, rowSpan[x]); + if (y > 25) + { + expectedColor = Color.Transparent; + } + + for (int x = 0; x < accessor.Width; x++) + { + Assert.Equal(expectedColor, rowSpan[x]); + } } - } + }); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs index 715ae58d7..87d665474 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs @@ -131,15 +131,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp where TPixel : unmanaged, IPixel { uint[] bgra = new uint[image.Width * image.Height]; - int idx = 0; - for (int y = 0; y < image.Height; y++) + image.ProcessPixelRows(accessor => { - Span rowSpan = image.DangerousGetRowSpan(y); - for (int x = 0; x < rowSpan.Length; x++) + int idx = 0; + for (int y = 0; y < accessor.Height; y++) { - bgra[idx++] = ToBgra32(rowSpan[x]).PackedValue; + Span rowSpan = accessor.GetRowSpan(y); + for (int x = 0; x < rowSpan.Length; x++) + { + bgra[idx++] = ToBgra32(rowSpan[x]).PackedValue; + } } - } + }); return bgra; } diff --git a/tests/ImageSharp.Tests/Image/ImageCloneTests.cs b/tests/ImageSharp.Tests/Image/ImageCloneTests.cs index 6d002dfee..8cca00a73 100644 --- a/tests/ImageSharp.Tests/Image/ImageCloneTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageCloneTests.cs @@ -32,15 +32,17 @@ namespace SixLabors.ImageSharp.Tests [WithTestPatternImages(9, 9, PixelTypes.Rgba32)] public void CloneAs_ToBgra32(TestImageProvider provider) { - using (Image image = provider.GetImage()) - using (Image clone = image.CloneAs()) + using Image image = provider.GetImage(); + using Image clone = image.CloneAs(); + + image.ProcessPixelRows(clone, static (imageAccessor, cloneAccessor) => { - for (int y = 0; y < image.Height; y++) + for (int y = 0; y < imageAccessor.Height; y++) { - Span row = image.DangerousGetRowSpan(y); - Span rowClone = clone.DangerousGetRowSpan(y); + Span row = imageAccessor.GetRowSpan(y); + Span rowClone = cloneAccessor.GetRowSpan(y); - for (int x = 0; x < image.Width; x++) + for (int x = 0; x < imageAccessor.Width; x++) { Rgba32 expected = row[x]; Bgra32 actual = rowClone[x]; @@ -51,22 +53,24 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(expected.A, actual.A); } } - } + }); } [Theory] [WithTestPatternImages(9, 9, PixelTypes.Rgba32)] public void CloneAs_ToBgr24(TestImageProvider provider) { - using (Image image = provider.GetImage()) - using (Image clone = image.CloneAs()) + using Image image = provider.GetImage(); + using Image clone = image.CloneAs(); + + image.ProcessPixelRows(clone, static (imageAccessor, cloneAccessor) => { - for (int y = 0; y < image.Height; y++) + for (int y = 0; y < imageAccessor.Height; y++) { - Span row = image.DangerousGetRowSpan(y); - Span rowClone = clone.DangerousGetRowSpan(y); + Span row = imageAccessor.GetRowSpan(y); + Span rowClone = cloneAccessor.GetRowSpan(y); - for (int x = 0; x < image.Width; x++) + for (int x = 0; x < cloneAccessor.Width; x++) { Rgba32 expected = row[x]; Bgr24 actual = rowClone[x]; @@ -76,22 +80,23 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(expected.B, actual.B); } } - } + }); } [Theory] [WithTestPatternImages(9, 9, PixelTypes.Rgba32)] public void CloneAs_ToArgb32(TestImageProvider provider) { - using (Image image = provider.GetImage()) - using (Image clone = image.CloneAs()) + using Image image = provider.GetImage(); + using Image clone = image.CloneAs(); + image.ProcessPixelRows(clone, static (imageAccessor, cloneAccessor) => { - for (int y = 0; y < image.Height; y++) + for (int y = 0; y < imageAccessor.Height; y++) { - Span row = image.DangerousGetRowSpan(y); - Span rowClone = clone.DangerousGetRowSpan(y); + Span row = imageAccessor.GetRowSpan(y); + Span rowClone = cloneAccessor.GetRowSpan(y); - for (int x = 0; x < image.Width; x++) + for (int x = 0; x < cloneAccessor.Width; x++) { Rgba32 expected = row[x]; Argb32 actual = rowClone[x]; @@ -102,22 +107,23 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(expected.A, actual.A); } } - } + }); } [Theory] [WithTestPatternImages(9, 9, PixelTypes.Rgba32)] public void CloneAs_ToRgb24(TestImageProvider provider) { - using (Image image = provider.GetImage()) - using (Image clone = image.CloneAs()) + using Image image = provider.GetImage(); + using Image clone = image.CloneAs(); + image.ProcessPixelRows(clone, static (imageAccessor, cloneAccessor) => { - for (int y = 0; y < image.Height; y++) + for (int y = 0; y < imageAccessor.Height; y++) { - Span row = image.DangerousGetRowSpan(y); - Span rowClone = clone.DangerousGetRowSpan(y); + Span row = imageAccessor.GetRowSpan(y); + Span rowClone = cloneAccessor.GetRowSpan(y); - for (int x = 0; x < image.Width; x++) + for (int x = 0; x < imageAccessor.Width; x++) { Rgba32 expected = row[x]; Rgb24 actual = rowClone[x]; @@ -127,7 +133,8 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(expected.B, actual.B); } } - } + }); + } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs index 9478fd949..dd1f5b701 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs @@ -163,10 +163,14 @@ namespace SixLabors.ImageSharp.Tests { Assert.Equal(memory, image.GetRootFramePixelBuffer().DangerousGetSingleMemory()); image.GetPixelMemoryGroup().Fill(bg); - for (var i = 10; i < 20; i++) + + image.ProcessPixelRows(accessor => { - image.DangerousGetRowSpan(i).Slice(10, 10).Fill(fg); - } + for (var i = 10; i < 20; i++) + { + accessor.GetRowSpan(i).Slice(10, 10).Fill(fg); + } + }); } Assert.False(memoryManager.IsDisposed); @@ -198,10 +202,13 @@ namespace SixLabors.ImageSharp.Tests { Assert.Equal(memoryManager.Memory, image.GetRootFramePixelBuffer().DangerousGetSingleMemory()); image.GetPixelMemoryGroup().Fill(bg); - for (var i = 10; i < 20; i++) + image.ProcessPixelRows(accessor => { - image.DangerousGetRowSpan(i).Slice(10, 10).Fill(fg); - } + for (var i = 10; i < 20; i++) + { + accessor.GetRowSpan(i).Slice(10, 10).Fill(fg); + } + }); } Assert.True(memoryManager.IsDisposed); @@ -263,10 +270,13 @@ namespace SixLabors.ImageSharp.Tests Assert.True(Unsafe.AreSame(ref pixelSpan.GetPinnableReference(), ref imageSpan.GetPinnableReference())); image.GetPixelMemoryGroup().Fill(bg); - for (var i = 10; i < 20; i++) + image.ProcessPixelRows(accessor => { - image.DangerousGetRowSpan(i).Slice(10, 10).Fill(fg); - } + for (var i = 10; i < 20; i++) + { + accessor.GetRowSpan(i).Slice(10, 10).Fill(fg); + } + }); } Assert.False(memoryManager.IsDisposed); @@ -332,10 +342,13 @@ namespace SixLabors.ImageSharp.Tests Assert.True(Unsafe.AreSame(ref pixelSpan.GetPinnableReference(), ref imageSpan.GetPinnableReference())); image.GetPixelMemoryGroup().Fill(bg); - for (var i = 10; i < 20; i++) + image.ProcessPixelRows(accessor => { - image.DangerousGetRowSpan(i).Slice(10, 10).Fill(fg); - } + for (var i = 10; i < 20; i++) + { + accessor.GetRowSpan(i).Slice(10, 10).Fill(fg); + } + }); } Assert.False(memoryManager.IsDisposed); @@ -413,16 +426,19 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(width, img.Width); Assert.Equal(height, img.Height); - for (int i = 0; i < height; ++i) + img.ProcessPixelRows(accessor => { - var arrayIndex = width * i; + for (int i = 0; i < height; ++i) + { + var arrayIndex = width * i; - Span rowSpan = img.DangerousGetRowSpan(i); - ref Rgba32 r0 = ref rowSpan[0]; - ref Rgba32 r1 = ref array[arrayIndex]; + Span rowSpan = accessor.GetRowSpan(i); + ref Rgba32 r0 = ref rowSpan[0]; + ref Rgba32 r1 = ref array[arrayIndex]; - Assert.True(Unsafe.AreSame(ref r0, ref r1)); - } + Assert.True(Unsafe.AreSame(ref r0, ref r1)); + } + }); } Assert.True(memory.Disposed); @@ -457,16 +473,19 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(width, img.Width); Assert.Equal(height, img.Height); - for (int i = 0; i < height; ++i) + img.ProcessPixelRows(acccessor => { - var arrayIndex = pixelSize * width * i; + for (int i = 0; i < height; ++i) + { + var arrayIndex = pixelSize * width * i; - Span rowSpan = img.DangerousGetRowSpan(i); - ref Rgba32 r0 = ref rowSpan[0]; - ref Rgba32 r1 = ref Unsafe.As(ref array[arrayIndex]); + Span rowSpan = acccessor.GetRowSpan(i); + ref Rgba32 r0 = ref rowSpan[0]; + ref Rgba32 r1 = ref Unsafe.As(ref array[arrayIndex]); - Assert.True(Unsafe.AreSame(ref r0, ref r1)); - } + Assert.True(Unsafe.AreSame(ref r0, ref r1)); + } + }); } Assert.True(memory.Disposed); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index 639e2f63c..7d6963a04 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -271,7 +271,6 @@ namespace SixLabors.ImageSharp.Tests // Image Assert.Throws(() => { var res = image.Clone(this.configuration); }); Assert.Throws(() => { var res = image.CloneAs(this.configuration); }); - Assert.Throws(() => { var res = image.DangerousGetRowSpan(default); }); Assert.Throws(() => { var res = image.DangerousTryGetSinglePixelMemory(out Memory _); }); // Image diff --git a/tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs b/tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs index e0626395d..53eb1da14 100644 --- a/tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs +++ b/tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs @@ -154,6 +154,23 @@ namespace SixLabors.ImageSharp.Tests } } + [Fact] + public void Disposed_ThrowsObjectDisposedException() + { + using var nonDisposed = new Image(1, 1); + var disposed = new Image(1, 1); + disposed.Dispose(); + + Assert.Throws(() => this.ProcessPixelRowsImpl(disposed, _ => { })); + + Assert.Throws(() => this.ProcessPixelRowsImpl(disposed, nonDisposed, (_, _) => { })); + Assert.Throws(() => this.ProcessPixelRowsImpl(nonDisposed, disposed, (_, _) => { })); + + Assert.Throws(() => this.ProcessPixelRowsImpl(disposed, nonDisposed, nonDisposed, (_, _, _) => { })); + Assert.Throws(() => this.ProcessPixelRowsImpl(nonDisposed, disposed, nonDisposed, (_, _, _) => { })); + Assert.Throws(() => this.ProcessPixelRowsImpl(nonDisposed, nonDisposed, disposed, (_, _, _) => { })); + } + [Fact] public void RetainsUnmangedBuffers1() { diff --git a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs index a9b3f1f36..b835aa63e 100644 --- a/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs @@ -93,25 +93,31 @@ namespace SixLabors.ImageSharp.Tests.Quantization Assert.Equal(1, result.Width); Assert.Equal(256, result.Height); - var actualImage = new Image(1, 256); + using var actualImage = new Image(1, 256); - ReadOnlySpan paletteSpan = result.Palette.Span; - int paletteCount = paletteSpan.Length - 1; - for (int y = 0; y < actualImage.Height; y++) + actualImage.ProcessPixelRows(accessor => { - Span row = actualImage.DangerousGetRowSpan(y); - ReadOnlySpan quantizedPixelSpan = result.DangerousGetRowSpan(y); - - for (int x = 0; x < actualImage.Width; x++) + ReadOnlySpan paletteSpan = result.Palette.Span; + int paletteCount = paletteSpan.Length - 1; + for (int y = 0; y < accessor.Height; y++) { - row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])]; + Span row = accessor.GetRowSpan(y); + ReadOnlySpan quantizedPixelSpan = result.DangerousGetRowSpan(y); + + for (int x = 0; x < accessor.Width; x++) + { + row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])]; + } } - } + }); - for (int y = 0; y < image.Height; y++) + image.ProcessPixelRows(actualImage, static (imageAccessor, actualImageAccessor) => { - Assert.True(image.DangerousGetRowSpan(y).SequenceEqual(actualImage.DangerousGetRowSpan(y))); - } + for (int y = 0; y < imageAccessor.Height; y++) + { + Assert.True(imageAccessor.GetRowSpan(y).SequenceEqual(actualImageAccessor.GetRowSpan(y))); + } + }); } [Theory] @@ -162,24 +168,30 @@ namespace SixLabors.ImageSharp.Tests.Quantization Assert.Equal(1, result.Width); Assert.Equal(256, result.Height); - ReadOnlySpan paletteSpan = result.Palette.Span; - int paletteCount = paletteSpan.Length - 1; - for (int y = 0; y < actualImage.Height; y++) + actualImage.ProcessPixelRows(accessor => { - Span row = actualImage.DangerousGetRowSpan(y); - ReadOnlySpan quantizedPixelSpan = result.DangerousGetRowSpan(y); - - for (int x = 0; x < actualImage.Width; x++) + ReadOnlySpan paletteSpan = result.Palette.Span; + int paletteCount = paletteSpan.Length - 1; + for (int y = 0; y < accessor.Height; y++) { - row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])]; + Span row = accessor.GetRowSpan(y); + ReadOnlySpan quantizedPixelSpan = result.DangerousGetRowSpan(y); + + for (int x = 0; x < accessor.Width; x++) + { + row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[x])]; + } } - } + }); } - for (int y = 0; y < expectedImage.Height; y++) + expectedImage.ProcessPixelRows(actualImage, static (expectedAccessor, actualAccessor) => { - Assert.True(expectedImage.DangerousGetRowSpan(y).SequenceEqual(actualImage.DangerousGetRowSpan(y))); - } + for (int y = 0; y < expectedAccessor.Height; y++) + { + Assert.True(expectedAccessor.GetRowSpan(y).SequenceEqual(actualAccessor.GetRowSpan(y))); + } + }); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs index eb310239b..3b8d0073e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs @@ -39,25 +39,29 @@ namespace SixLabors.ImageSharp.Tests public override Image GetImage() { var result = new Image(this.Configuration, this.Width, this.Height); + result.ProcessPixelRows(accessor => + { + int midY = this.Height / 2; + int midX = this.Width / 2; - int midY = this.Height / 2; - int midX = this.Width / 2; + for (int y = 0; y < midY; y++) + { + Span row = accessor.GetRowSpan(y); - for (int y = 0; y < midY; y++) - { - Span row = result.DangerousGetRowSpan(y); + row.Slice(0, midX).Fill(TopLeftColor); + row.Slice(midX, this.Width - midX).Fill(TopRightColor); + } - row.Slice(0, midX).Fill(TopLeftColor); - row.Slice(midX, this.Width - midX).Fill(TopRightColor); - } + for (int y = midY; y < this.Height; y++) + { + Span row = accessor.GetRowSpan(y); + + row.Slice(0, midX).Fill(BottomLeftColor); + row.Slice(midX, this.Width - midX).Fill(BottomRightColor); + } + }); - for (int y = midY; y < this.Height; y++) - { - Span row = result.DangerousGetRowSpan(y); - row.Slice(0, midX).Fill(BottomLeftColor); - row.Slice(midX, this.Width - midX).Fill(BottomRightColor); - } return result; }