From 6e2076f40d5aaf6641ed390621bb757b6d829808 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 19 May 2017 13:01:35 +0100 Subject: [PATCH 1/2] Load from pixel data methods --- src/ImageSharp/Image/Image.LoadPixelData.cs | 100 ++++++++++++++++++ .../PixelFormats/PixelOperations{TPixel}.cs | 22 ++++ .../ImageSharp.Tests/Image/ImageLoadTests.cs | 42 ++++++++ 3 files changed, 164 insertions(+) create mode 100644 src/ImageSharp/Image/Image.LoadPixelData.cs diff --git a/src/ImageSharp/Image/Image.LoadPixelData.cs b/src/ImageSharp/Image/Image.LoadPixelData.cs new file mode 100644 index 0000000000..fd79791907 --- /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 993a11232a..b29af94ee0 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 4cdf529e6d..7a5b8ffc53 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; From ea5cc6f3a71393c6c4a758d81c8d1c40dd91868f Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 19 May 2017 13:56:13 +0100 Subject: [PATCH 2/2] updated to make better use of Span --- src/ImageSharp/Image/Image.LoadPixelData.cs | 40 +++++-------------- .../PixelFormats/PixelOperations{TPixel}.cs | 22 ---------- 2 files changed, 10 insertions(+), 52 deletions(-) diff --git a/src/ImageSharp/Image/Image.LoadPixelData.cs b/src/ImageSharp/Image/Image.LoadPixelData.cs index fd79791907..75aa318bed 100644 --- a/src/ImageSharp/Image/Image.LoadPixelData.cs +++ b/src/ImageSharp/Image/Image.LoadPixelData.cs @@ -14,49 +14,36 @@ namespace ImageSharp using ImageSharp.PixelFormats; /// - /// Adds static methods allowing the creation of new image from a byte array. + /// Adds static methods allowing the creation of new image from raw pixel data. /// public static partial class Image { /// - /// Create a new instance of the class from the given pixel data. + /// Create a new instance of the class from the raw 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) + public static Image LoadPixelData(Span 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. + /// Create a new instance of the class from the given byte array in format. /// /// 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) + public static Image LoadPixelData(Span 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. + /// Create a new instance of the class from the given byte array in format. /// /// The config for the decoder. /// The byte array containing image data. @@ -64,19 +51,12 @@ namespace ImageSharp /// The height of the final image. /// The pixel format. /// A new . - public static Image LoadPixelData(Configuration config, byte[] data, int width, int height) + public static Image LoadPixelData(Configuration config, Span 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); - } - } + => LoadPixelData(config, data.NonPortableCast(), width, height); /// - /// Create a new instance of the class from the given byte array as raw pixel data. + /// Create a new instance of the class from the raw data. /// /// The config for the decoder. /// The Span containing the image Pixel data. @@ -84,7 +64,7 @@ namespace ImageSharp /// The height of the final image. /// The pixel format. /// A new . - private static Image LoadPixelData(Configuration config, Span data, int width, int height) + public static Image LoadPixelData(Configuration config, Span data, int width, int height) where TPixel : struct, IPixel { int count = width * height; diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index b29af94ee0..993a11232a 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -249,27 +249,5 @@ 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