diff --git a/src/ImageSharp/Advanced/ImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
similarity index 99%
rename from src/ImageSharp/Advanced/ImageExtensions.cs
rename to src/ImageSharp/Advanced/AdvancedImageExtensions.cs
index 8ab245aee..511e66c64 100644
--- a/src/ImageSharp/Advanced/ImageExtensions.cs
+++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
@@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Advanced
///
/// Extension methods over Image{TPixel}
///
- public static class ImageExtensions
+ public static class AdvancedImageExtensions
{
///
/// Returns a reference to the 0th element of the Pixel buffer,
diff --git a/src/ImageSharp/Image/ImageExtensions.cs b/src/ImageSharp/Image/ImageExtensions.cs
index c5b3d6f31..c4de1c298 100644
--- a/src/ImageSharp/Image/ImageExtensions.cs
+++ b/src/ImageSharp/Image/ImageExtensions.cs
@@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp
}
///
- /// Saves the raw image to the given bytes.
+ /// Saves the raw image pixels to a byte array in row-major order.
///
/// The Pixel format.
/// The source image
@@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp
=> source.GetPixelSpan().AsBytes().ToArray();
///
- /// Saves the raw image to the given bytes.
+ /// Saves the raw image pixels to the given byte array in row-major order.
///
/// The Pixel format.
/// The source image
@@ -131,26 +131,21 @@ namespace SixLabors.ImageSharp
/// Thrown if the stream is null.
public static void SavePixelData(this ImageFrame source, byte[] buffer)
where TPixel : struct, IPixel
- => SavePixelData(source, new Span(buffer));
+ => SavePixelData(source, buffer.AsSpan().NonPortableCast());
///
- /// Saves the raw image to the given bytes.
+ /// Saves the raw image pixels to the given TPixel array in row-major order.
///
/// The Pixel format.
/// The source image
/// The buffer to save the raw pixel data to.
/// Thrown if the stream is null.
- private static void SavePixelData(this ImageFrame source, Span buffer)
+ public static void SavePixelData(this ImageFrame source, TPixel[] buffer)
where TPixel : struct, IPixel
- {
- Span byteBuffer = source.GetPixelSpan().AsBytes();
- Guard.MustBeGreaterThanOrEqualTo(buffer.Length, byteBuffer.Length, nameof(buffer));
-
- byteBuffer.CopyTo(buffer);
- }
+ => SavePixelData(source, new Span(buffer));
///
- /// Saves the raw image to the given bytes.
+ /// Saves the raw image pixels to a byte array in row-major order.
///
/// The Pixel format.
/// The source image
@@ -161,7 +156,7 @@ namespace SixLabors.ImageSharp
=> source.Frames.RootFrame.SavePixelData();
///
- /// Saves the raw image to the given bytes.
+ /// Saves the raw image pixels to the given byte array in row-major order.
///
/// The Pixel format.
/// The source image
@@ -172,13 +167,13 @@ namespace SixLabors.ImageSharp
=> source.Frames.RootFrame.SavePixelData(buffer);
///
- /// Saves the raw image to the given bytes.
+ /// Saves the raw image pixels to the given TPixel array in row-major order.
///
/// The Pixel format.
/// The source image
/// The buffer to save the raw pixel data to.
/// Thrown if the stream is null.
- private static void SavePixelData(this Image source, Span buffer)
+ public static void SavePixelData(this Image source, TPixel[] buffer)
where TPixel : struct, IPixel
=> source.Frames.RootFrame.SavePixelData(buffer);
@@ -200,5 +195,32 @@ namespace SixLabors.ImageSharp
return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(stream.ToArray())}";
}
}
+
+ ///
+ /// Saves the raw image to the given bytes.
+ ///
+ /// The Pixel format.
+ /// The source image
+ /// The buffer to save the raw pixel data to.
+ /// Thrown if the stream is null.
+ internal static void SavePixelData(this Image source, Span buffer)
+ where TPixel : struct, IPixel
+ => source.Frames.RootFrame.SavePixelData(buffer.NonPortableCast());
+
+ ///
+ /// Saves the raw image to the given bytes.
+ ///
+ /// The Pixel format.
+ /// The source image
+ /// The buffer to save the raw pixel data to.
+ /// Thrown if the stream is null.
+ internal static void SavePixelData(this ImageFrame source, Span buffer)
+ where TPixel : struct, IPixel
+ {
+ Span sourceBuffer = source.GetPixelSpan();
+ Guard.MustBeGreaterThanOrEqualTo(buffer.Length, sourceBuffer.Length, nameof(buffer));
+
+ sourceBuffer.CopyTo(buffer);
+ }
}
}
diff --git a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
new file mode 100644
index 000000000..4291e775d
--- /dev/null
+++ b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using Xunit;
+using SixLabors.ImageSharp.Advanced;
+using System.Runtime.CompilerServices;
+// ReSharper disable InconsistentNaming
+
+namespace SixLabors.ImageSharp.Tests.Advanced
+{
+ using SixLabors.ImageSharp.PixelFormats;
+
+ public class AdvancedImageExtensionsTests
+ {
+ [Theory]
+ [WithTestPatternImages(131, 127, PixelTypes.Rgba32 | PixelTypes.Bgr24)]
+ public unsafe void DangerousGetPinnableReference_CopyToBuffer(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage())
+ {
+ TPixel[] targetBuffer = new TPixel[image.Width * image.Height];
+
+ ref byte source = ref Unsafe.As(ref targetBuffer[0]);
+ ref byte dest = ref Unsafe.As(ref image.DangerousGetPinnableReferenceToPixelBuffer());
+
+ fixed (byte* targetPtr = &source)
+ fixed (byte* pixelBasePtr = &dest)
+ {
+ uint dataSizeInBytes = (uint)(image.Width * image.Height * Unsafe.SizeOf());
+ Unsafe.CopyBlock(targetPtr, pixelBasePtr, dataSizeInBytes);
+ }
+
+ image.ComparePixelBufferTo(targetBuffer);
+ }
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Advanced/ImageExtensionsTests.cs b/tests/ImageSharp.Tests/Advanced/ImageExtensionsTests.cs
deleted file mode 100644
index 3bd6bf19b..000000000
--- a/tests/ImageSharp.Tests/Advanced/ImageExtensionsTests.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using Xunit;
-using SixLabors.ImageSharp.Advanced;
-using System.Runtime.CompilerServices;
-
-namespace SixLabors.ImageSharp.Tests.Advanced
-{
- public class ImageExtensionsTests
- {
- [Fact]
- public unsafe void DangerousGetPinnableReference_CopyToBuffer()
- {
- var image = new Image(128, 128);
- for (int y = 0; y < image.Height; y++)
- for (int x = 0; x < image.Width; x++)
- {
- image[x, y] = new Rgba32(x, 255 - y, x + y);
- }
-
- Rgba32[] targetBuffer = new Rgba32[image.Width * image.Height];
-
- fixed (Rgba32* targetPtr = targetBuffer)
- fixed (Rgba32* pixelBasePtr = &image.DangerousGetPinnableReferenceToPixelBuffer())
- {
- uint dataSizeInBytes = (uint)(image.Width * image.Height * Unsafe.SizeOf());
- Unsafe.CopyBlock(targetPtr, pixelBasePtr, dataSizeInBytes);
- }
-
- for (int y = 0; y < image.Height; y++)
- for (int x = 0; x < image.Width; x++)
- {
- int linearIndex = y * image.Width + x;
- Assert.Equal(image[x, y], targetBuffer[linearIndex]);
- }
- }
- }
-}
diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs
index 36d3b3c05..5b672059c 100644
--- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs
+++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs
@@ -9,9 +9,12 @@ using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.PixelFormats;
using Moq;
using Xunit;
+// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
+ using System.Runtime.CompilerServices;
+
///
/// Tests the class.
///
@@ -47,76 +50,39 @@ namespace SixLabors.ImageSharp.Tests
this.Image = new Image(config, 1, 1);
}
- [Fact]
- public void SavePixelData_Rgba32()
+ [Theory]
+ [WithTestPatternImages(13, 19, PixelTypes.Rgba32 | PixelTypes.Bgr24)]
+ public void SavePixelData_ToPixelStructArray(TestImageProvider provider)
+ where TPixel : struct, IPixel
{
- using (var img = new Image(2, 2))
+ using (Image image = provider.GetImage())
{
- img[0, 0] = Rgba32.White;
- img[1, 0] = Rgba32.Black;
+ TPixel[] buffer = new TPixel[image.Width*image.Height];
+ image.SavePixelData(buffer);
- img[0, 1] = Rgba32.Red;
- img[1, 1] = Rgba32.Blue;
- var buffer = new byte[2 * 2 * 4]; // width * height * bytes per pixel
- img.SavePixelData(buffer);
-
- Assert.Equal(255, buffer[0]); // 0, 0, R
- Assert.Equal(255, buffer[1]); // 0, 0, G
- Assert.Equal(255, buffer[2]); // 0, 0, B
- Assert.Equal(255, buffer[3]); // 0, 0, A
-
- Assert.Equal(0, buffer[4]); // 1, 0, R
- Assert.Equal(0, buffer[5]); // 1, 0, G
- Assert.Equal(0, buffer[6]); // 1, 0, B
- Assert.Equal(255, buffer[7]); // 1, 0, A
-
- Assert.Equal(255, buffer[8]); // 0, 1, R
- Assert.Equal(0, buffer[9]); // 0, 1, G
- Assert.Equal(0, buffer[10]); // 0, 1, B
- Assert.Equal(255, buffer[11]); // 0, 1, A
-
- Assert.Equal(0, buffer[12]); // 1, 1, R
- Assert.Equal(0, buffer[13]); // 1, 1, G
- Assert.Equal(255, buffer[14]); // 1, 1, B
- Assert.Equal(255, buffer[15]); // 1, 1, A
+ image.ComparePixelBufferTo(buffer);
+
+ // TODO: We need a separate test-case somewhere ensuring that image pixels are stored in row-major order!
}
}
-
- [Fact]
- public void SavePixelData_Bgr24()
+ [Theory]
+ [WithTestPatternImages(19, 13, PixelTypes.Rgba32 | PixelTypes.Bgr24)]
+ public void SavePixelData_ToByteArray(TestImageProvider provider)
+ where TPixel : struct, IPixel
{
- using (var img = new Image(2, 2))
+ using (Image image = provider.GetImage())
{
- img[0, 0] = NamedColors.White;
- img[1, 0] = NamedColors.Black;
-
- img[0, 1] = NamedColors.Red;
- img[1, 1] = NamedColors.Blue;
-
- var buffer = new byte[2 * 2 * 3]; // width * height * bytes per pixel
- img.SavePixelData(buffer);
+ byte[] buffer = new byte[image.Width*image.Height*Unsafe.SizeOf()];
- Assert.Equal(255, buffer[0]); // 0, 0, B
- Assert.Equal(255, buffer[1]); // 0, 0, G
- Assert.Equal(255, buffer[2]); // 0, 0, R
+ image.SavePixelData(buffer);
- Assert.Equal(0, buffer[3]); // 1, 0, B
- Assert.Equal(0, buffer[4]); // 1, 0, G
- Assert.Equal(0, buffer[5]); // 1, 0, R
-
- Assert.Equal(0, buffer[6]); // 0, 1, B
- Assert.Equal(0, buffer[7]); // 0, 1, G
- Assert.Equal(255, buffer[8]); // 0, 1, R
-
- Assert.Equal(255, buffer[9]); // 1, 1, B
- Assert.Equal(0, buffer[10]); // 1, 1, G
- Assert.Equal(0, buffer[11]); // 1, 1, R
+ image.ComparePixelBufferTo(buffer.AsSpan().NonPortableCast());
}
}
-
+
[Fact]
- public void SavePixelData_Rgba32_Buffer_must_be_bigger()
+ public void SavePixelData_Rgba32_WhenBufferIsTooSmall_Throws()
{
using (var img = new Image(2, 2))
{
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
index b3dd763c7..b367ecbd9 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
@@ -17,6 +17,8 @@ namespace SixLabors.ImageSharp.Tests
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
+ using Xunit;
+
public static class TestImageExtensions
{
///
@@ -81,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests
grayscale,
appendPixelTypeToFileName);
}
-
+
///
/// Compares the image against the expected Reference output, throws an exception if the images are not similar enough.
/// The output file should be named identically to the output produced by .
@@ -153,6 +155,24 @@ namespace SixLabors.ImageSharp.Tests
}
}
+ public static Image ComparePixelBufferTo(
+ this Image image,
+ Span expectedPixels)
+ where TPixel : struct, IPixel
+ {
+ Span actual = image.GetPixelSpan();
+
+ Assert.True(expectedPixels.Length == actual.Length, "Buffer sizes are not equal!");
+
+ for (int i = 0; i < expectedPixels.Length; i++)
+ {
+ Assert.True(expectedPixels[i].Equals(actual[i]), $"Pixels are different on position {i}!" );
+ }
+
+ return image;
+ }
+
+
public static Image CompareToOriginal(
this Image image,
ITestImageProvider provider)
@@ -160,7 +180,7 @@ namespace SixLabors.ImageSharp.Tests
{
return CompareToOriginal(image, provider, ImageComparer.Tolerant());
}
-
+
public static Image CompareToOriginal(
this Image image,
ITestImageProvider provider,