diff --git a/src/ImageSharp/Image/Image.LoadPixelData.cs b/src/ImageSharp/Image/Image.LoadPixelData.cs
new file mode 100644
index 000000000..fd7979190
--- /dev/null
+++ b/src/ImageSharp/Image/Image.LoadPixelData.cs
@@ -0,0 +1,100 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.IO;
+ using System.Runtime.CompilerServices;
+ using System.Threading.Tasks;
+ using Formats;
+ using ImageSharp.Memory;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Adds static methods allowing the creation of new image from a byte array.
+ ///
+ public static partial class Image
+ {
+ ///
+ /// Create a new instance of the class from the given pixel data.
+ ///
+ /// The byte array containing image data.
+ /// The width of the final image.
+ /// The height of the final image.
+ /// The pixel format.
+ /// A new .
+ public static Image LoadPixelData(TPixel[] data, int width, int height)
+ where TPixel : struct, IPixel
+ => LoadPixelData(Configuration.Default, data, width, height);
+
+ ///
+ /// Create a new instance of the class from the given pixel data.
+ ///
+ /// The config for the decoder.
+ /// The byte array containing image data.
+ /// The width of the final image.
+ /// The height of the final image.
+ /// The pixel format.
+ /// A new .
+ public static Image LoadPixelData(Configuration config, TPixel[] data, int width, int height)
+ where TPixel : struct, IPixel
+ => LoadPixelData(config, new Span(data), width, height);
+
+ ///
+ /// Create a new instance of the class from the given byte array as raw pixel data.
+ ///
+ /// The byte array containing image data.
+ /// The width of the final image.
+ /// The height of the final image.
+ /// The pixel format.
+ /// A new .
+ public static Image LoadPixelData(byte[] data, int width, int height)
+ where TPixel : struct, IPixel
+ => LoadPixelData(Configuration.Default, data, width, height);
+
+ ///
+ /// Create a new instance of the class from the given byte array as raw pixel data.
+ ///
+ /// The config for the decoder.
+ /// The byte array containing image data.
+ /// The width of the final image.
+ /// The height of the final image.
+ /// The pixel format.
+ /// A new .
+ public static Image LoadPixelData(Configuration config, byte[] data, int width, int height)
+ where TPixel : struct, IPixel
+ {
+ int size = width * height;
+ using (var sourceBuffer = new Buffer(size))
+ {
+ PixelOperations.Instance.PackFromRawBytes(new Span(data), sourceBuffer.Span, size);
+ return LoadPixelData(config, sourceBuffer.Span, width, height);
+ }
+ }
+
+ ///
+ /// Create a new instance of the class from the given byte array as raw pixel data.
+ ///
+ /// The config for the decoder.
+ /// The Span containing the image Pixel data.
+ /// The width of the final image.
+ /// The height of the final image.
+ /// The pixel format.
+ /// A new .
+ private static Image LoadPixelData(Configuration config, Span data, int width, int height)
+ where TPixel : struct, IPixel
+ {
+ int count = width * height;
+ Guard.MustBeGreaterThanOrEqualTo(data.Length, width * height, nameof(data));
+ var image = new Image(config, width, height);
+ var dest = new Span(image.Pixels, 0, count);
+
+ SpanHelper.Copy(data, dest, count);
+
+ return image;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
index 993a11232..b29af94ee 100644
--- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
+++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
@@ -249,5 +249,27 @@ namespace ImageSharp.PixelFormats
sp.ToZyxwBytes(destBytes, i * 4);
}
}
+
+ ///
+ /// Bulk conversion of data from series bytes to a series of Pixels.
+ ///
+ /// The to the source bytes.
+ /// The to the destination colors.
+ /// The number of pixels to convert.
+ internal virtual void PackFromRawBytes(Span sourceBytes, Span destColors, int count)
+ {
+ Guard.MustBeSizedAtLeast(sourceBytes, count * Unsafe.SizeOf(), nameof(sourceBytes));
+ Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors));
+
+ ref TPixel sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference());
+ ref TPixel destRef = ref destColors.DangerousGetPinnableReference();
+
+ for (int i = 0; i < count; i++)
+ {
+ ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
+ ref TPixel dp = ref Unsafe.Add(ref destRef, i);
+ dp = sp;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs
index 4cdf529e6..7a5b8ffc5 100644
--- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs
+++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs
@@ -526,6 +526,48 @@ namespace ImageSharp.Tests
this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, this.decoderOptions));
}
+ [Fact]
+ public void LoadFromPixelData_Pixels()
+ {
+ var img = Image.LoadPixelData(new Rgba32[] {
+ Rgba32.Black, Rgba32.White,
+ Rgba32.White, Rgba32.Black,
+ }, 2, 2);
+
+ Assert.NotNull(img);
+ using (var px = img.Lock())
+ {
+ Assert.Equal(Rgba32.Black, px[0, 0]);
+ Assert.Equal(Rgba32.White, px[0, 1]);
+
+ Assert.Equal(Rgba32.White, px[1, 0]);
+ Assert.Equal(Rgba32.Black, px[1, 1]);
+
+ }
+ }
+
+ [Fact]
+ public void LoadFromPixelData_Bytes()
+ {
+ var img = Image.LoadPixelData(new byte[] {
+ 0,0,0,255, // 0,0
+ 255,255,255,255, // 0,1
+ 255,255,255,255, // 1,0
+ 0,0,0,255, // 1,1
+ }, 2, 2);
+
+ Assert.NotNull(img);
+ using (var px = img.Lock())
+ {
+ Assert.Equal(Rgba32.Black, px[0, 0]);
+ Assert.Equal(Rgba32.White, px[0, 1]);
+
+ Assert.Equal(Rgba32.White, px[1, 0]);
+ Assert.Equal(Rgba32.Black, px[1, 1]);
+
+ }
+ }
+
public void Dispose()
{
// clean up the global object;