diff --git a/README.md b/README.md index 0287db86f..b6c2c2185 100644 --- a/README.md +++ b/README.md @@ -177,3 +177,14 @@ Please... Spread the word, contribute algorithms, submit performance improvement Performance is a biggie, if you know anything about the new vector types and can apply some fancy new stuff with that it would be awesome. There's a lot of developers out there who could write this stuff a lot better and faster than I and I would love to see what we collectively can come up with so please, if you can help in any way it would be most welcome and benificial for all. + +###The ImageProcessor Team + +Grand High Eternal Dictator +- [James Jackson-South](https://github.com/jimbobsquarepants) + +Core Team +- [Yufeih Huang](https://github.com/yufeih) +- [Thomas Broust](https://github.com/cosmo0) +- [Christopher Bauer](https://github.com/christopherbauer) +- [Jeavon Leopold](https://github.com/jeavon) diff --git a/src/ImageProcessorCore/Colors/Color.cs b/src/ImageProcessorCore/Colors/Color.cs index 5b35205d9..36cb548fb 100644 --- a/src/ImageProcessorCore/Colors/Color.cs +++ b/src/ImageProcessorCore/Colors/Color.cs @@ -78,20 +78,24 @@ namespace ImageProcessorCore if (hex.Length == 8) { - float r = Convert.ToByte(hex.Substring(2, 2), 16) / 255f; - float g = Convert.ToByte(hex.Substring(4, 2), 16) / 255f; - float b = Convert.ToByte(hex.Substring(6, 2), 16) / 255f; - float a = Convert.ToByte(hex.Substring(0, 2), 16) / 255f; + float r = Convert.ToByte(hex.Substring(2, 2), 16); + float g = Convert.ToByte(hex.Substring(4, 2), 16); + float b = Convert.ToByte(hex.Substring(6, 2), 16); + float a = Convert.ToByte(hex.Substring(0, 2), 16); - this.backingVector = FromNonPremultiplied(new Color(r, g, b, a)).ToVector4(); + // Do division of Vector4 instead of each component to utilize SIMD optimizations + this.backingVector = FromNonPremultiplied(new Vector4(r, g, b, a) / 255f, this.A); } else if (hex.Length == 6) { - this.R = Convert.ToByte(hex.Substring(0, 2), 16) / 255f; - this.G = Convert.ToByte(hex.Substring(2, 2), 16) / 255f; - this.B = Convert.ToByte(hex.Substring(4, 2), 16) / 255f; - this.A = 1; + float r = Convert.ToByte(hex.Substring(0, 2), 16); + float g = Convert.ToByte(hex.Substring(2, 2), 16); + float b = Convert.ToByte(hex.Substring(4, 2), 16); + float a = 255f; + + // Do division of Vector4 instead of each component to utilize SIMD optimizations + this.backingVector = new Vector4(r, g, b, a) / 255f; } else { @@ -99,10 +103,12 @@ namespace ImageProcessorCore string gh = char.ToString(hex[1]); string bh = char.ToString(hex[2]); - this.B = Convert.ToByte(bh + bh, 16) / 255f; - this.G = Convert.ToByte(gh + gh, 16) / 255f; - this.R = Convert.ToByte(rh + rh, 16) / 255f; - this.A = 1; + float r = Convert.ToByte(rh + rh, 16); + float g = Convert.ToByte(gh + gh, 16); + float b = Convert.ToByte(bh + bh, 16); + float a = 255f; + + this.backingVector = new Vector4(r, g, b, a) / 255f; } } @@ -123,7 +129,7 @@ namespace ImageProcessorCore /// public Color(Vector3 vector) { - this.backingVector = new Vector4(vector.X, vector.Y, vector.Z, 1); + this.backingVector = new Vector4(vector, 1); } /// @@ -135,7 +141,7 @@ namespace ImageProcessorCore /// The alpha component. public Color(Vector3 vector, float alpha) { - this.backingVector = new Vector4(vector.X, vector.Y, vector.Z, alpha); + this.backingVector = new Vector4(vector, alpha); } /// @@ -367,8 +373,18 @@ namespace ImageProcessorCore /// The . public static Color FromNonPremultiplied(Color color) { - float a = color.A; - return new Color(color.backingVector * new Vector4(a, a, a, 1)); + return new Color(FromNonPremultiplied(color.backingVector, color.A)); + } + + /// + /// Converts a non-premultiplied alpha Vector4 to a Vector4 that contains premultiplied alpha. + /// + /// The vector to convert. + /// The alpha to use in conversion. + /// The Vector4 with premultiplied alpha. + private static Vector4 FromNonPremultiplied(Vector4 vector, float alpha) + { + return vector * new Vector4(alpha, alpha, alpha, 1); } /// @@ -443,10 +459,12 @@ namespace ImageProcessorCore /// public bool AlmostEquals(Color other, float precision) { - return Math.Abs(this.R - other.R) < precision - && Math.Abs(this.G - other.G) < precision - && Math.Abs(this.B - other.B) < precision - && Math.Abs(this.A - other.A) < precision; + Vector4 result = Vector4.Abs(this.backingVector - other.backingVector); + + return result.X < precision + && result.Y < precision + && result.Z < precision + && result.W < precision; } /// diff --git a/src/ImageProcessorCore/Colors/Colorspaces/CieLab.cs b/src/ImageProcessorCore/Colors/Colorspaces/CieLab.cs index 2f684e45c..cce92601a 100644 --- a/src/ImageProcessorCore/Colors/Colorspaces/CieLab.cs +++ b/src/ImageProcessorCore/Colors/Colorspaces/CieLab.cs @@ -172,9 +172,11 @@ namespace ImageProcessorCore /// public bool AlmostEquals(CieLab other, float precision) { - return Math.Abs(this.L - other.L) < precision - && Math.Abs(this.B - other.B) < precision - && Math.Abs(this.B - other.B) < precision; + Vector3 result = Vector3.Abs(this.backingVector - other.backingVector); + + return result.X < precision + && result.Y < precision + && result.Z < precision; } /// diff --git a/src/ImageProcessorCore/Colors/Colorspaces/CieXyz.cs b/src/ImageProcessorCore/Colors/Colorspaces/CieXyz.cs index a14c04bdd..8f41c8abb 100644 --- a/src/ImageProcessorCore/Colors/Colorspaces/CieXyz.cs +++ b/src/ImageProcessorCore/Colors/Colorspaces/CieXyz.cs @@ -163,9 +163,11 @@ namespace ImageProcessorCore /// public bool AlmostEquals(CieXyz other, float precision) { - return Math.Abs(this.X - other.X) < precision - && Math.Abs(this.Y - other.Y) < precision - && Math.Abs(this.Z - other.Z) < precision; + Vector3 result = Vector3.Abs(this.backingVector - other.backingVector); + + return result.X < precision + && result.Y < precision + && result.Z < precision; } /// diff --git a/src/ImageProcessorCore/Colors/Colorspaces/Cmyk.cs b/src/ImageProcessorCore/Colors/Colorspaces/Cmyk.cs index 1d8e8d852..b343288a6 100644 --- a/src/ImageProcessorCore/Colors/Colorspaces/Cmyk.cs +++ b/src/ImageProcessorCore/Colors/Colorspaces/Cmyk.cs @@ -175,10 +175,12 @@ namespace ImageProcessorCore /// public bool AlmostEquals(Cmyk other, float precision) { - return Math.Abs(this.C - other.C) < precision - && Math.Abs(this.M - other.M) < precision - && Math.Abs(this.Y - other.Y) < precision - && Math.Abs(this.K - other.K) < precision; + Vector4 result = Vector4.Abs(this.backingVector - other.backingVector); + + return result.X < precision + && result.Y < precision + && result.Z < precision + && result.W < precision; } /// diff --git a/src/ImageProcessorCore/Colors/Colorspaces/Hsl.cs b/src/ImageProcessorCore/Colors/Colorspaces/Hsl.cs index e585ecf4d..e6eee42e3 100644 --- a/src/ImageProcessorCore/Colors/Colorspaces/Hsl.cs +++ b/src/ImageProcessorCore/Colors/Colorspaces/Hsl.cs @@ -192,9 +192,11 @@ namespace ImageProcessorCore /// public bool AlmostEquals(Hsl other, float precision) { - return Math.Abs(this.H - other.H) < precision - && Math.Abs(this.S - other.S) < precision - && Math.Abs(this.L - other.L) < precision; + Vector3 result = Vector3.Abs(this.backingVector - other.backingVector); + + return result.X < precision + && result.Y < precision + && result.Z < precision; } /// diff --git a/src/ImageProcessorCore/Colors/Colorspaces/Hsv.cs b/src/ImageProcessorCore/Colors/Colorspaces/Hsv.cs index 8ce2183de..914df51c9 100644 --- a/src/ImageProcessorCore/Colors/Colorspaces/Hsv.cs +++ b/src/ImageProcessorCore/Colors/Colorspaces/Hsv.cs @@ -186,9 +186,11 @@ namespace ImageProcessorCore /// public bool AlmostEquals(Hsv other, float precision) { - return Math.Abs(this.H - other.H) < precision - && Math.Abs(this.S - other.S) < precision - && Math.Abs(this.V - other.V) < precision; + Vector3 result = Vector3.Abs(this.backingVector - other.backingVector); + + return result.X < precision + && result.Y < precision + && result.Z < precision; } /// diff --git a/src/ImageProcessorCore/Colors/Colorspaces/YCbCr.cs b/src/ImageProcessorCore/Colors/Colorspaces/YCbCr.cs index 5cef084cc..5c47c6c08 100644 --- a/src/ImageProcessorCore/Colors/Colorspaces/YCbCr.cs +++ b/src/ImageProcessorCore/Colors/Colorspaces/YCbCr.cs @@ -162,9 +162,11 @@ namespace ImageProcessorCore /// public bool AlmostEquals(YCbCr other, float precision) { - return Math.Abs(this.Y - other.Y) < precision - && Math.Abs(this.Cb - other.Cb) < precision - && Math.Abs(this.Cr - other.Cr) < precision; + Vector3 result = Vector3.Abs(this.backingVector - other.backingVector); + + return result.X < precision + && result.Y < precision + && result.Z < precision; } /// diff --git a/src/ImageProcessorCore/Filters/Alpha.cs b/src/ImageProcessorCore/Filters/Alpha.cs index 5e2861bd2..a4d6fc3d6 100644 --- a/src/ImageProcessorCore/Filters/Alpha.cs +++ b/src/ImageProcessorCore/Filters/Alpha.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore.Filters using System.Threading.Tasks; /// - /// An to change the Alpha of an . + /// An to change the Alpha of an . /// public class Alpha : ParallelImageProcessorCore { diff --git a/src/ImageProcessorCore/Filters/Binarization/Threshold.cs b/src/ImageProcessorCore/Filters/Binarization/Threshold.cs index a234d1b38..203b89918 100644 --- a/src/ImageProcessorCore/Filters/Binarization/Threshold.cs +++ b/src/ImageProcessorCore/Filters/Binarization/Threshold.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore.Filters using System.Threading.Tasks; /// - /// An to perform binary threshold filtering against an + /// An to perform binary threshold filtering against an /// . The image will be converted to greyscale before thresholding /// occurs. /// diff --git a/src/ImageProcessorCore/Filters/Brightness.cs b/src/ImageProcessorCore/Filters/Brightness.cs index def8d0237..9499f49fa 100644 --- a/src/ImageProcessorCore/Filters/Brightness.cs +++ b/src/ImageProcessorCore/Filters/Brightness.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore.Filters using System.Threading.Tasks; /// - /// An to change the brightness of an . + /// An to change the brightness of an . /// public class Brightness : ParallelImageProcessorCore { diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/IColorMatrixFilter.cs b/src/ImageProcessorCore/Filters/ColorMatrix/IColorMatrixFilter.cs index 2f5d695eb..0845b521b 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/IColorMatrixFilter.cs +++ b/src/ImageProcessorCore/Filters/ColorMatrix/IColorMatrixFilter.cs @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Filters /// Encapsulates properties and methods for creating processors that utilize a matrix to /// alter the image pixels. /// - public interface IColorMatrixFilter : IImageProcessorCore + public interface IColorMatrixFilter : IImageProcessor { /// /// Gets the used to alter the image. diff --git a/src/ImageProcessorCore/Filters/ColorMatrix/Saturation.cs b/src/ImageProcessorCore/Filters/ColorMatrix/Saturation.cs index 9729d68ac..f5e6f1b3c 100644 --- a/src/ImageProcessorCore/Filters/ColorMatrix/Saturation.cs +++ b/src/ImageProcessorCore/Filters/ColorMatrix/Saturation.cs @@ -9,7 +9,7 @@ namespace ImageProcessorCore.Filters using System.Numerics; /// - /// An to change the saturation of an . + /// An to change the saturation of an . /// public class Saturation : ColorMatrixFilter { diff --git a/src/ImageProcessorCore/Filters/Contrast.cs b/src/ImageProcessorCore/Filters/Contrast.cs index 96c8a243a..baf1e1131 100644 --- a/src/ImageProcessorCore/Filters/Contrast.cs +++ b/src/ImageProcessorCore/Filters/Contrast.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore.Filters using System.Threading.Tasks; /// - /// An to change the contrast of an . + /// An to change the contrast of an . /// public class Contrast : ParallelImageProcessorCore { diff --git a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/IEdgeDetectorFilter.cs b/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/IEdgeDetectorFilter.cs index 8986f8725..0016dad66 100644 --- a/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/IEdgeDetectorFilter.cs +++ b/src/ImageProcessorCore/Filters/Convolution/EdgeDetection/IEdgeDetectorFilter.cs @@ -8,7 +8,7 @@ namespace ImageProcessorCore.Filters /// /// Provides properties and methods allowing the detection of edges within an image. /// - public interface IEdgeDetectorFilter : IImageProcessorCore + public interface IEdgeDetectorFilter : IImageProcessor { /// /// Gets or sets a value indicating whether to convert the diff --git a/src/ImageProcessorCore/Filters/ImageFilterExtensions.cs b/src/ImageProcessorCore/Filters/ImageFilterExtensions.cs index 4a186e8c6..0da5f6b58 100644 --- a/src/ImageProcessorCore/Filters/ImageFilterExtensions.cs +++ b/src/ImageProcessorCore/Filters/ImageFilterExtensions.cs @@ -326,8 +326,8 @@ namespace ImageProcessorCore.Filters /// The . public static Image Greyscale(this Image source, Rectangle rectangle, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null) { - IImageProcessorCore processor = mode == GreyscaleMode.Bt709 - ? (IImageProcessorCore)new GreyscaleBt709() + IImageProcessor processor = mode == GreyscaleMode.Bt709 + ? (IImageProcessor)new GreyscaleBt709() : new GreyscaleBt601(); processor.OnProgress += progressHandler; diff --git a/src/ImageProcessorCore/Filters/Invert.cs b/src/ImageProcessorCore/Filters/Invert.cs index 609844738..f6ba6b52a 100644 --- a/src/ImageProcessorCore/Filters/Invert.cs +++ b/src/ImageProcessorCore/Filters/Invert.cs @@ -9,7 +9,7 @@ namespace ImageProcessorCore.Filters using System.Threading.Tasks; /// - /// An to invert the colors of an . + /// An to invert the colors of an . /// public class Invert : ParallelImageProcessorCore { diff --git a/src/ImageProcessorCore/Filters/Pixelate.cs b/src/ImageProcessorCore/Filters/Pixelate.cs index 97eba0ce1..8f49f5cf1 100644 --- a/src/ImageProcessorCore/Filters/Pixelate.cs +++ b/src/ImageProcessorCore/Filters/Pixelate.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore.Filters using System.Threading.Tasks; /// - /// An to invert the colors of an . + /// An to invert the colors of an . /// public class Pixelate : ParallelImageProcessorCore { diff --git a/src/ImageProcessorCore/IImageProcessor.cs b/src/ImageProcessorCore/IImageProcessor.cs index 7eefb86e5..6eb649e84 100644 --- a/src/ImageProcessorCore/IImageProcessor.cs +++ b/src/ImageProcessorCore/IImageProcessor.cs @@ -15,7 +15,7 @@ namespace ImageProcessorCore /// /// Encapsulates methods to alter the pixels of an image. /// - public interface IImageProcessorCore + public interface IImageProcessor { /// /// Event fires when each row of the source image has been processed. diff --git a/src/ImageProcessorCore/Image.cs b/src/ImageProcessorCore/Image.cs index cfae7cdd0..44c597ed3 100644 --- a/src/ImageProcessorCore/Image.cs +++ b/src/ImageProcessorCore/Image.cs @@ -230,52 +230,45 @@ namespace ImageProcessorCore /// private void Load(Stream stream, IList formats) { - try + if (!stream.CanRead) { - if (!stream.CanRead) - { - throw new NotSupportedException("Cannot read from the stream."); - } + throw new NotSupportedException("Cannot read from the stream."); + } - if (!stream.CanSeek) - { - throw new NotSupportedException("The stream does not support seeking."); - } + if (!stream.CanSeek) + { + throw new NotSupportedException("The stream does not support seeking."); + } - if (formats.Count > 0) + if (formats.Count > 0) + { + int maxHeaderSize = formats.Max(x => x.Decoder.HeaderSize); + if (maxHeaderSize > 0) { - int maxHeaderSize = formats.Max(x => x.Decoder.HeaderSize); - if (maxHeaderSize > 0) + byte[] header = new byte[maxHeaderSize]; + + stream.Read(header, 0, maxHeaderSize); + stream.Position = 0; + + IImageFormat format = formats.FirstOrDefault(x => x.Decoder.IsSupportedFileFormat(header)); + if (format != null) { - byte[] header = new byte[maxHeaderSize]; - - stream.Read(header, 0, maxHeaderSize); - stream.Position = 0; - - IImageFormat format = formats.FirstOrDefault(x => x.Decoder.IsSupportedFileFormat(header)); - if (format != null) - { - format.Decoder.Decode(this, stream); - this.CurrentImageFormat = format; - return; - } + format.Decoder.Decode(this, stream); + this.CurrentImageFormat = format; + return; } } + } - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.AppendLine("Image cannot be loaded. Available formats:"); - - foreach (IImageFormat format in formats) - { - stringBuilder.AppendLine("-" + format); - } + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendLine("Image cannot be loaded. Available formats:"); - throw new NotSupportedException(stringBuilder.ToString()); - } - finally + foreach (IImageFormat format in formats) { - stream.Dispose(); + stringBuilder.AppendLine("-" + format); } + + throw new NotSupportedException(stringBuilder.ToString()); } } } diff --git a/src/ImageProcessorCore/ImageExtensions.cs b/src/ImageProcessorCore/ImageExtensions.cs index a986462cf..cf36f8030 100644 --- a/src/ImageProcessorCore/ImageExtensions.cs +++ b/src/ImageProcessorCore/ImageExtensions.cs @@ -55,7 +55,7 @@ namespace ImageProcessorCore /// The image this method extends. /// Any processors to apply to the image. /// The . - public static Image Process(this Image source, params IImageProcessorCore[] processors) + public static Image Process(this Image source, params IImageProcessor[] processors) { return Process(source, source.Bounds, processors); } @@ -70,10 +70,10 @@ namespace ImageProcessorCore /// /// Any processors to apply to the image. /// The . - public static Image Process(this Image source, Rectangle sourceRectangle, params IImageProcessorCore[] processors) + public static Image Process(this Image source, Rectangle sourceRectangle, params IImageProcessor[] processors) { // ReSharper disable once LoopCanBeConvertedToQuery - foreach (IImageProcessorCore filter in processors) + foreach (IImageProcessor filter in processors) { source = PerformAction(source, true, (sourceImage, targetImage) => filter.Apply(targetImage, sourceImage, sourceRectangle)); } @@ -89,7 +89,7 @@ namespace ImageProcessorCore /// The target image height. /// Any processors to apply to the image. /// The . - public static Image Process(this Image source, int width, int height, params IImageProcessorCore[] processors) + public static Image Process(this Image source, int width, int height, params IImageProcessor[] processors) { return Process(source, width, height, source.Bounds, default(Rectangle), processors); } @@ -113,10 +113,10 @@ namespace ImageProcessorCore /// /// Any processors to apply to the image. /// The . - public static Image Process(this Image source, int width, int height, Rectangle sourceRectangle, Rectangle targetRectangle, params IImageProcessorCore[] processors) + public static Image Process(this Image source, int width, int height, Rectangle sourceRectangle, Rectangle targetRectangle, params IImageProcessor[] processors) { // ReSharper disable once LoopCanBeConvertedToQuery - foreach (IImageProcessorCore filter in processors) + foreach (IImageProcessor filter in processors) { source = PerformAction(source, false, (sourceImage, targetImage) => filter.Apply(targetImage, sourceImage, width, height, targetRectangle, sourceRectangle)); } diff --git a/src/ImageProcessorCore/ParallelImageProcessor.cs b/src/ImageProcessorCore/ParallelImageProcessor.cs index 6dce1b1bd..41b802cde 100644 --- a/src/ImageProcessorCore/ParallelImageProcessor.cs +++ b/src/ImageProcessorCore/ParallelImageProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore /// /// Allows the application of processors using parallel processing. /// - public abstract class ParallelImageProcessorCore : IImageProcessorCore + public abstract class ParallelImageProcessorCore : IImageProcessor { /// public event ProgressEventHandler OnProgress; diff --git a/tests/ImageProcessorCore.Tests/Colors/ColorTests.cs b/tests/ImageProcessorCore.Tests/Colors/ColorTests.cs index 9ab1e8da4..0ff680b18 100644 --- a/tests/ImageProcessorCore.Tests/Colors/ColorTests.cs +++ b/tests/ImageProcessorCore.Tests/Colors/ColorTests.cs @@ -8,6 +8,8 @@ // // -------------------------------------------------------------------------------------------------------------------- +using System.Numerics; + namespace ImageProcessorCore.Tests { using Xunit; @@ -76,6 +78,24 @@ namespace ImageProcessorCore.Tests Assert.Equal(0, color3.G, 1); Assert.Equal(0, color3.B, 3); Assert.Equal(1, color3.A, 1); + + Color color4 = new Color(new Vector3(1, .1f, .133f)); + Assert.Equal(1, color4.R, 1); + Assert.Equal(.1f, color4.G, 1); + Assert.Equal(.133f, color4.B, 3); + Assert.Equal(1, color4.A, 1); + + Color color5 = new Color(new Vector3(1, .1f, .133f), .5f); + Assert.Equal(1, color5.R, 1); + Assert.Equal(.1f, color5.G, 1); + Assert.Equal(.133f, color5.B, 3); + Assert.Equal(.5f, color5.A, 1); + + Color color6 = new Color(new Vector4(1, .1f, .133f, .5f)); + Assert.Equal(1, color5.R, 1); + Assert.Equal(.1f, color6.G, 1); + Assert.Equal(.133f, color6.B, 3); + Assert.Equal(.5f, color6.A, 1); } /// diff --git a/tests/ImageProcessorCore.Tests/Processors/Filters/FilterTests.cs b/tests/ImageProcessorCore.Tests/Processors/Filters/FilterTests.cs index 5101212ad..9214afdaa 100644 --- a/tests/ImageProcessorCore.Tests/Processors/Filters/FilterTests.cs +++ b/tests/ImageProcessorCore.Tests/Processors/Filters/FilterTests.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore.Tests public class FilterTests : ProcessorTestBase { - public static readonly TheoryData Filters = new TheoryData + public static readonly TheoryData Filters = new TheoryData { { "Brightness-50", new Brightness(50) }, { "Brightness--50", new Brightness(-50) }, @@ -49,7 +49,7 @@ namespace ImageProcessorCore.Tests [Theory] [MemberData("Filters")] - public void FilterImage(string name, IImageProcessorCore processor) + public void FilterImage(string name, IImageProcessor processor) { if (!Directory.Exists("TestOutput/Filter")) { diff --git a/tests/ImageProcessorCore.Tests/Processors/Formats/EncoderDecoderTests.cs b/tests/ImageProcessorCore.Tests/Processors/Formats/EncoderDecoderTests.cs index 0899955f4..310fa4a3b 100644 --- a/tests/ImageProcessorCore.Tests/Processors/Formats/EncoderDecoderTests.cs +++ b/tests/ImageProcessorCore.Tests/Processors/Formats/EncoderDecoderTests.cs @@ -6,7 +6,7 @@ using Formats; using Xunit; - + using System.Linq; public class EncoderDecoderTests : ProcessorTestBase { [Fact] @@ -101,5 +101,39 @@ } } } + + [Fact] + public void ImageShouldPreservePixelByteOrderWhenSerialized() + { + if (!Directory.Exists("TestOutput/Serialized")) + { + Directory.CreateDirectory("TestOutput/Serialized"); + } + + foreach (string file in Files) + { + using (FileStream stream = File.OpenRead(file)) + { + Image image = new Image(stream); + byte[] serialized; + using (MemoryStream memoryStream = new MemoryStream()) + { + image.Save(memoryStream); + memoryStream.Flush(); + serialized = memoryStream.ToArray(); + } + + using (MemoryStream memoryStream = new MemoryStream(serialized)) + { + Image image2 = new Image(memoryStream); + + using (FileStream output = File.OpenWrite($"TestOutput/Serialized/{Path.GetFileName(file)}")) + { + image2.Save(output); + } + } + } + } + } } } \ No newline at end of file