diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index 0273f02f56..a79733fec3 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -78,84 +78,6 @@ public static IMemoryGroup GetPixelMemoryGroup(this Image => source?.Frames.RootFrame.GetPixelMemoryGroup() ?? throw new ArgumentNullException(nameof(source)); - /// - /// 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. - /// - /// The type of the pixel. - /// The source image. - /// The - /// Thrown when the backing buffer is discontiguous. - [Obsolete( - @"GetPixelSpan might fail, because the backing buffer could be discontiguous for large images. Use GetPixelMemoryGroup or GetPixelRowSpan instead!")] - public static Span GetPixelSpan(this ImageFrame source) - where TPixel : unmanaged, IPixel - { - Guard.NotNull(source, nameof(source)); - - IMemoryGroup mg = source.GetPixelMemoryGroup(); - if (mg.Count > 1) - { - throw new InvalidOperationException($"GetPixelSpan is invalid, since the backing buffer of this {source.Width}x{source.Height} sized image is discontiguous!"); - } - - return mg.Single().Span; - } - - /// - /// Gets the representation of the pixels as a of contiguous memory in the source image's pixel format - /// stored in row major order. - /// - /// The type of the pixel. - /// The source. - /// The - /// Thrown when the backing buffer is discontiguous. - [Obsolete( - @"GetPixelSpan might fail, because the backing buffer could be discontiguous for large images. Use GetPixelMemoryGroup or GetPixelRowSpan instead!")] - public static Span GetPixelSpan(this Image source) - where TPixel : unmanaged, IPixel - { - Guard.NotNull(source, nameof(source)); - - return source.Frames.RootFrame.GetPixelSpan(); - } - - /// - /// Gets the representation of the pixels as a of contiguous memory - /// at row beginning from the the first pixel on that row. - /// - /// The type of the pixel. - /// The source. - /// The row. - /// The - public static Span GetPixelRowSpan(this ImageFrame source, int rowIndex) - where TPixel : unmanaged, IPixel - { - Guard.NotNull(source, nameof(source)); - Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex)); - Guard.MustBeLessThan(rowIndex, source.Height, nameof(rowIndex)); - - return source.PixelBuffer.GetRowSpan(rowIndex); - } - - /// - /// Gets the representation of the pixels as of of contiguous memory - /// at row beginning from the the first pixel on that row. - /// - /// The type of the pixel. - /// The source. - /// The row. - /// The - public static Span GetPixelRowSpan(this Image source, int rowIndex) - where TPixel : unmanaged, IPixel - { - Guard.NotNull(source, nameof(source)); - Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex)); - Guard.MustBeLessThan(rowIndex, source.Height, nameof(rowIndex)); - - return source.Frames.RootFrame.PixelBuffer.GetRowSpan(rowIndex); - } - /// /// Gets the representation of the pixels as a of contiguous memory /// at row beginning from the the first pixel on that row. diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index a35443ec9d..0171f3d07f 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; @@ -166,6 +167,40 @@ internal ImageFrame(Configuration configuration, ImageFrame source) } } + /// + /// Gets the representation of the pixels as a of contiguous memory + /// at row beginning from the first pixel on that row. + /// + /// The row. + /// The + /// Thrown when row index is out of range. + public Span GetPixelRowSpan(int rowIndex) + { + Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex)); + Guard.MustBeLessThan(rowIndex, this.Height, nameof(rowIndex)); + + return this.PixelBuffer.GetRowSpan(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. + /// + /// The . + /// The . + public bool TryGetSinglePixelSpan(out Span span) + { + IMemoryGroup mg = this.GetPixelMemoryGroup(); + if (mg.Count > 1) + { + span = default; + return false; + } + + span = mg.Single().Span; + return true; + } + /// /// Gets a reference to the pixel at the specified position. /// diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 56f1f6b7bf..f6173db972 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -163,6 +163,40 @@ internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable< } } + /// + /// Gets the representation of the pixels as a of contiguous memory + /// at row beginning from the first pixel on that row. + /// + /// The row. + /// The + /// Thrown when row index is out of range. + public Span GetPixelRowSpan(int rowIndex) + { + Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex)); + Guard.MustBeLessThan(rowIndex, this.Height, nameof(rowIndex)); + + return this.PixelSource.PixelBuffer.GetRowSpan(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. + /// + /// The . + /// The . + public bool TryGetSinglePixelSpan(out Span span) + { + IMemoryGroup mg = this.GetPixelMemoryGroup(); + if (mg.Count > 1) + { + span = default; + return false; + } + + span = mg.Single().Span; + return true; + } + /// /// Clones the current image /// diff --git a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs index dca124849a..97731be94e 100644 --- a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs +++ b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs @@ -61,7 +61,10 @@ public void ConsumedMemory_PixelDataIsCorrect(TestImageProvider { using Image image0 = provider.GetImage(); var targetBuffer = new TPixel[image0.Width * image0.Height]; - image0.GetPixelSpan().CopyTo(targetBuffer); + + Assert.True(image0.TryGetSinglePixelSpan(out Span sourceBuffer)); + + sourceBuffer.CopyTo(targetBuffer); var managerOfExternalMemory = new TestMemoryManager(targetBuffer); diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index c3bc2f2ae2..d08d81899b 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -128,7 +128,8 @@ public void WorksWithDifferentLocations(TestImageProvider provider, int using (Image background = provider.GetImage()) using (var overlay = new Image(50, 50)) { - overlay.GetPixelSpan().Fill(Color.Black); + Assert.True(overlay.TryGetSinglePixelSpan(out Span overlaySpan)); + overlaySpan.Fill(Color.Black); background.Mutate(c => c.DrawImage(overlay, new Point(x, y), PixelColorBlendingMode.Normal, 1F)); diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index fa28993720..b3a99aa1c0 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -164,7 +164,10 @@ public void CanDecodeIntermingledImages() { ImageFrame first = kumin1.Frames[i]; ImageFrame second = kumin2.Frames[i]; - first.ComparePixelBufferTo(second.GetPixelSpan()); + + Assert.True(second.TryGetSinglePixelSpan(out Span secondSpan)); + + first.ComparePixelBufferTo(secondSpan); } } } diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs index c69cf2f20b..48171a77dc 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs @@ -9,6 +9,7 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Tga { @@ -46,7 +47,8 @@ public static Image DecodeWithMagick(Configuration configuration { magickImage.AutoOrient(); var result = new Image(configuration, magickImage.Width, magickImage.Height); - Span resultPixels = result.GetPixelSpan(); + + Assert.True(result.TryGetSinglePixelSpan(out Span resultPixels)); using (IPixelCollection pixels = magickImage.GetPixelsUnsafe()) { diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs index 9ea6a305c3..2b7c1e2c60 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs @@ -198,7 +198,9 @@ public void CloneFrame(TestImageProvider provider) using (Image cloned = img.Frames.CloneFrame(0)) { Assert.Equal(2, img.Frames.Count); - cloned.ComparePixelBufferTo(img.GetPixelSpan()); + Assert.True(img.TryGetSinglePixelSpan(out Span imgSpan)); + + cloned.ComparePixelBufferTo(imgSpan); } } } @@ -210,7 +212,8 @@ public void ExtractFrame(TestImageProvider provider) { using (Image img = provider.GetImage()) { - var sourcePixelData = img.GetPixelSpan().ToArray(); + Assert.True(img.TryGetSinglePixelSpan(out Span imgSpan)); + TPixel[] sourcePixelData = imgSpan.ToArray(); img.Frames.AddFrame(new ImageFrame(Configuration.Default, 10, 10)); using (Image cloned = img.Frames.ExportFrame(0)) @@ -242,7 +245,8 @@ public void CreateFrame_CustomFillColor() [Fact] public void AddFrameFromPixelData() { - var pixelData = this.Image.Frames.RootFrame.GetPixelSpan().ToArray(); + Assert.True(this.Image.Frames.RootFrame.TryGetSinglePixelSpan(out Span imgSpan)); + var pixelData = imgSpan.ToArray(); this.Image.Frames.AddFrame(pixelData); Assert.Equal(2, this.Image.Frames.Count); } @@ -251,8 +255,10 @@ public void AddFrameFromPixelData() public void AddFrame_clones_sourceFrame() { var otherFrame = new ImageFrame(Configuration.Default, 10, 10); - var addedFrame = this.Image.Frames.AddFrame(otherFrame); - addedFrame.ComparePixelBufferTo(otherFrame.GetPixelSpan()); + ImageFrame addedFrame = this.Image.Frames.AddFrame(otherFrame); + + Assert.True(otherFrame.TryGetSinglePixelSpan(out Span otherFrameSpan)); + addedFrame.ComparePixelBufferTo(otherFrameSpan); Assert.NotEqual(otherFrame, addedFrame); } @@ -260,8 +266,10 @@ public void AddFrame_clones_sourceFrame() public void InsertFrame_clones_sourceFrame() { var otherFrame = new ImageFrame(Configuration.Default, 10, 10); - var addedFrame = this.Image.Frames.InsertFrame(0, otherFrame); - addedFrame.ComparePixelBufferTo(otherFrame.GetPixelSpan()); + ImageFrame addedFrame = this.Image.Frames.InsertFrame(0, otherFrame); + + Assert.True(otherFrame.TryGetSinglePixelSpan(out Span otherFrameSpan)); + addedFrame.ComparePixelBufferTo(otherFrameSpan); Assert.NotEqual(otherFrame, addedFrame); } diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs index 08e6f8e1f0..a05b428e15 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs @@ -159,7 +159,8 @@ public void CloneFrame(TestImageProvider provider) var expectedClone = (Image)cloned; - expectedClone.ComparePixelBufferTo(img.GetPixelSpan()); + Assert.True(img.TryGetSinglePixelSpan(out Span imgSpan)); + expectedClone.ComparePixelBufferTo(imgSpan); } } } @@ -171,7 +172,8 @@ public void ExtractFrame(TestImageProvider provider) { using (Image img = provider.GetImage()) { - var sourcePixelData = img.GetPixelSpan().ToArray(); + Assert.True(img.TryGetSinglePixelSpan(out Span imgSpan)); + var sourcePixelData = imgSpan.ToArray(); ImageFrameCollection nonGenericFrameCollection = img.Frames; diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs index 423309dfb8..e0152558b5 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs @@ -91,7 +91,8 @@ public void WrapMemory_CreatedImageIsCorrect() using (var image = Image.WrapMemory(cfg, memory, 5, 5, metaData)) { - ref Rgba32 pixel0 = ref image.GetPixelSpan()[0]; + Assert.True(image.TryGetSinglePixelSpan(out Span imageSpan)); + ref Rgba32 pixel0 = ref imageSpan[0]; Assert.True(Unsafe.AreSame(ref array[0], ref pixel0)); Assert.Equal(cfg, image.GetConfiguration()); @@ -118,7 +119,8 @@ public void WrapSystemDrawingBitmap_WhenObserved() using (var image = Image.WrapMemory(memory, bmp.Width, bmp.Height)) { Assert.Equal(memory, image.GetRootFramePixelBuffer().GetSingleMemory()); - image.GetPixelSpan().Fill(bg); + Assert.True(image.TryGetSinglePixelSpan(out Span imageSpan)); + imageSpan.Fill(bg); for (var i = 10; i < 20; i++) { image.GetPixelRowSpan(i).Slice(10, 10).Fill(fg); @@ -153,8 +155,8 @@ public void WrapSystemDrawingBitmap_WhenOwned() using (var image = Image.WrapMemory(memoryManager, bmp.Width, bmp.Height)) { Assert.Equal(memoryManager.Memory, image.GetRootFramePixelBuffer().GetSingleMemory()); - - image.GetPixelSpan().Fill(bg); + Assert.True(image.TryGetSinglePixelSpan(out Span imageSpan)); + imageSpan.Fill(bg); for (var i = 10; i < 20; i++) { image.GetPixelRowSpan(i).Slice(10, 10).Fill(fg); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index c99b75733a..6bba8b15c7 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -27,7 +27,8 @@ public void Width_Height() { Assert.Equal(11, image.Width); Assert.Equal(23, image.Height); - Assert.Equal(11 * 23, image.GetPixelSpan().Length); + Assert.True(image.TryGetSinglePixelSpan(out Span imageSpan)); + Assert.Equal(11 * 23, imageSpan.Length); image.ComparePixelBufferTo(default(Rgba32)); Assert.Equal(Configuration.Default, image.GetConfiguration()); @@ -43,7 +44,8 @@ public void Configuration_Width_Height() { Assert.Equal(11, image.Width); Assert.Equal(23, image.Height); - Assert.Equal(11 * 23, image.GetPixelSpan().Length); + Assert.True(image.TryGetSinglePixelSpan(out Span imageSpan)); + Assert.Equal(11 * 23, imageSpan.Length); image.ComparePixelBufferTo(default(Rgba32)); Assert.Equal(configuration, image.GetConfiguration()); @@ -60,7 +62,8 @@ public void Configuration_Width_Height_BackgroundColor() { Assert.Equal(11, image.Width); Assert.Equal(23, image.Height); - Assert.Equal(11 * 23, image.GetPixelSpan().Length); + Assert.True(image.TryGetSinglePixelSpan(out Span imageSpan)); + Assert.Equal(11 * 23, imageSpan.Length); image.ComparePixelBufferTo(color); Assert.Equal(configuration, image.GetConfiguration()); diff --git a/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs b/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs index c8a8baf1d1..7352ddd7df 100644 --- a/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs +++ b/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using Xunit; @@ -17,5 +18,15 @@ public void CreateAndResize(TestImageProvider provider) image.Mutate(c => c.Resize(1000, 1000)); image.DebugSave(provider); } + + [Theory] + [WithBasicTestPatternImages(width: 10, height: 10, PixelTypes.Rgba32)] + public void GetSingleSpan(TestImageProvider provider) + { + provider.LimitAllocatorBufferCapacity().InPixels(10); + using Image image = provider.GetImage(); + Assert.False(image.TryGetSinglePixelSpan(out Span imageSpan)); + Assert.False(image.Frames.RootFrame.TryGetSinglePixelSpan(out Span imageFrameSpan)); + } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs index cdc77d3216..2de903d664 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs @@ -241,7 +241,7 @@ private static IResampler GetResampler(string name) private static void VerifyAllPixelsAreWhiteOrTransparent(Image image) where TPixel : unmanaged, IPixel { - Span data = image.Frames.RootFrame.GetPixelSpan(); + Assert.True(image.Frames.RootFrame.TryGetSinglePixelSpan(out Span data)); var white = new Rgb24(255, 255, 255); foreach (TPixel pixel in data) { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 655bcfea4b..3f323000ab 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -244,7 +244,8 @@ public void Resize_ThrowsForWrappedMemoryImage(TestImageProvider { using (Image image0 = provider.GetImage()) { - var mmg = TestMemoryManager.CreateAsCopyOf(image0.GetPixelSpan()); + Assert.True(image0.TryGetSinglePixelSpan(out Span imageSpan)); + var mmg = TestMemoryManager.CreateAsCopyOf(imageSpan); using (var image1 = Image.WrapMemory(mmg.Memory, image0.Width, image0.Height)) { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 8ecb2ee6ce..fbdb512dea 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -417,8 +417,7 @@ public static Image ComparePixelBufferTo( Span expectedPixels) where TPixel : unmanaged, IPixel { - Span actualPixels = image.GetPixelSpan(); - + Assert.True(image.TryGetSinglePixelSpan(out Span actualPixels)); CompareBuffers(expectedPixels, actualPixels); return image; @@ -478,7 +477,7 @@ public static Image ComparePixelBufferTo(this Image imag public static ImageFrame ComparePixelBufferTo(this ImageFrame imageFrame, TPixel expectedPixel) where TPixel : unmanaged, IPixel { - Span actualPixels = imageFrame.GetPixelSpan(); + Assert.True(imageFrame.TryGetSinglePixelSpan(out Span actualPixels)); for (int i = 0; i < actualPixels.Length; i++) { @@ -493,8 +492,7 @@ public static ImageFrame ComparePixelBufferTo( Span expectedPixels) where TPixel : unmanaged, IPixel { - Span actual = image.GetPixelSpan(); - + Assert.True(image.TryGetSinglePixelSpan(out Span actual)); Assert.True(expectedPixels.Length == actual.Length, "Buffer sizes are not equal!"); for (int i = 0; i < expectedPixels.Length; i++) @@ -677,8 +675,7 @@ internal static Image ToGrayscaleImage(this Buffer2D buffer, floa { var image = new Image(buffer.Width, buffer.Height); - Span pixels = image.Frames.RootFrame.GetPixelSpan(); - + Assert.True(image.Frames.RootFrame.TryGetSinglePixelSpan(out Span pixels)); Span bufferSpan = buffer.GetSingleSpan(); for (int i = 0; i < bufferSpan.Length; i++) diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs index 3a1fbd1952..8af20a94fd 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -14,6 +14,7 @@ using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using Xunit; namespace SixLabors.ImageSharp.Tests { @@ -160,7 +161,7 @@ internal static void RunBufferCapacityLimitProcessorTest( ImageComparer comparer = null) where TPixel : unmanaged, IPixel { - comparer??= ImageComparer.Exact; + comparer ??= ImageComparer.Exact; using Image expected = provider.GetImage(); int width = expected.Width; expected.Mutate(process); @@ -277,7 +278,8 @@ public static void RunValidatingProcessorTestOnWrappedMemoryImage( using (Image image0 = provider.GetImage()) { - var mmg = TestMemoryManager.CreateAsCopyOf(image0.GetPixelSpan()); + Assert.True(image0.TryGetSinglePixelSpan(out Span imageSpan)); + var mmg = TestMemoryManager.CreateAsCopyOf(imageSpan); using (var image1 = Image.WrapMemory(mmg.Memory, image0.Width, image0.Height)) {