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;