diff --git a/src/ImageSharp/Image/ImageExtensions.cs b/src/ImageSharp/Image/ImageExtensions.cs
index 1c1d7b1475..f46f86ccac 100644
--- a/src/ImageSharp/Image/ImageExtensions.cs
+++ b/src/ImageSharp/Image/ImageExtensions.cs
@@ -4,7 +4,9 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Runtime.CompilerServices;
using System.Text;
+using SixLabors.ImageSharp.Advanced.Unsafe;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@@ -129,6 +131,22 @@ namespace SixLabors.ImageSharp
source.Save(stream, encoder);
}
+ ///
+ /// 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.
+ public static void SavePixelData(this Image source, Span buffer)
+ where TPixel : struct, IPixel
+ {
+ Span byteBuffer = source.GetPixelSpan().AsBytes();
+ Guard.MustBeGreaterThanOrEqualTo(buffer.Length, byteBuffer.Length, nameof(buffer));
+
+ byteBuffer.CopyTo(buffer);
+ }
+
///
/// Returns a Base64 encoded string from the given image.
///
diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs
index aa691aa6d0..ed4770879e 100644
--- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs
+++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs
@@ -318,6 +318,7 @@ namespace SixLabors.ImageSharp.Tests
}
}
+
[Fact]
public void LoadsImageWithoutThrowingCrcException()
{
diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs
index dd7a0eae59..36d3b3c056 100644
--- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs
+++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs
@@ -47,6 +47,93 @@ namespace SixLabors.ImageSharp.Tests
this.Image = new Image(config, 1, 1);
}
+ [Fact]
+ public void SavePixelData_Rgba32()
+ {
+ using (var img = new Image(2, 2))
+ {
+ img[0, 0] = Rgba32.White;
+ img[1, 0] = Rgba32.Black;
+
+ 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
+ }
+ }
+
+
+ [Fact]
+ public void SavePixelData_Bgr24()
+ {
+ using (var img = new Image(2, 2))
+ {
+ 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);
+
+ Assert.Equal(255, buffer[0]); // 0, 0, B
+ Assert.Equal(255, buffer[1]); // 0, 0, G
+ Assert.Equal(255, buffer[2]); // 0, 0, R
+
+ 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
+ }
+ }
+
+ [Fact]
+ public void SavePixelData_Rgba32_Buffer_must_be_bigger()
+ {
+ using (var img = new Image(2, 2))
+ {
+ img[0, 0] = Rgba32.White;
+ img[1, 0] = Rgba32.Black;
+
+ img[0, 1] = Rgba32.Red;
+ img[1, 1] = Rgba32.Blue;
+ var buffer = new byte[2 * 2]; // width * height * bytes per pixel
+
+ Assert.Throws(() =>
+ {
+ img.SavePixelData(buffer);
+ });
+ }
+ }
+
[Fact]
public void SavePath()
{