// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) James South. // Licensed under the Apache License, Version 2.0. // // -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.UnitTests { using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using ImageProcessor.Imaging; using ImageProcessor.Imaging.Filters.EdgeDetection; using ImageProcessor.Imaging.Filters.Photo; using FluentAssertions; using NUnit.Framework; /// /// Test harness for the image factory /// [TestFixture] public class ImageFactoryUnitTests { /// /// The list of images. Designed to speed up the tests a little. /// private IEnumerable imagesInfos; /// /// The list of ImageFactories. Designed to speed up the test a bit more. /// private List imagesFactories; /// /// Tests the loading of image from a file /// [Test] public void TestLoadImageFromFile() { foreach (FileInfo file in this.ListInputFiles()) { using (ImageFactory imageFactory = new ImageFactory()) { imageFactory.Load(file.FullName); imageFactory.ImagePath.Should().Be(file.FullName, "because the path should have been memorized"); imageFactory.Image.Should().NotBeNull("because the image should have been loaded"); } } } /// /// Tests the loading of image from a memory stream /// [Test] public void TestLoadImageFromMemory() { foreach (FileInfo file in this.ListInputFiles()) { byte[] photoBytes = File.ReadAllBytes(file.FullName); using (MemoryStream inStream = new MemoryStream(photoBytes)) { using (ImageFactory imageFactory = new ImageFactory()) { imageFactory.Load(inStream); imageFactory.ImagePath.Should().BeNull("because an image loaded from stream should not have a file path"); imageFactory.Image.Should().NotBeNull("because the image should have been loaded"); } } } } /// /// Tests that the save method actually saves a file /// [Test] public void TestSaveToDisk() { foreach (FileInfo file in this.ListInputFiles()) { string outputFileName = string.Format("./output/{0}", file.Name); using (ImageFactory imageFactory = new ImageFactory()) { imageFactory.Load(file.FullName); imageFactory.Save(outputFileName); File.Exists(outputFileName).Should().BeTrue("because the file should have been saved on disk"); File.Delete(outputFileName); } } } /// /// Tests that the save method actually writes to memory /// [Test] public void TestSaveToMemory() { foreach (ImageFactory imageFactory in this.ListInputImages()) { using (MemoryStream s = new MemoryStream()) { imageFactory.Save(s); s.Seek(0, SeekOrigin.Begin); s.Capacity.Should().BeGreaterThan(0, "because the stream should contain the image"); } } } /// /// Tests that a filter is really applied by checking that the image is modified /// [Test] public void TestApplyEffectAlpha() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Alpha(50); AssertImagesAreDifferent(original, imageFactory.Image, "because the alpha operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that brightness changes is really applied by checking that the image is modified /// [Test] public void TestApplyEffectBrightness() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Brightness(50); AssertImagesAreDifferent(original, imageFactory.Image, "because the brightness operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that background color changes are really applied by checking that the image is modified /// [Test] public void TestApplyEffectBackgroundColor() { ImageFactory imageFactory = new ImageFactory(); imageFactory.Load(@"Images\text.png"); Image original = (Image)imageFactory.Image.Clone(); imageFactory.BackgroundColor(Color.Yellow); AssertImagesAreDifferent(original, imageFactory.Image, "because the background color operation should have been applied on {0}", imageFactory.ImagePath); } /// /// Tests that a contrast change is really applied by checking that the image is modified /// [Test] public void TestApplyEffectContrast() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Contrast(50); AssertImagesAreDifferent(original, imageFactory.Image, "because the contrast operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that a saturation change is really applied by checking that the image is modified /// [Test] public void TestApplyEffectSaturation() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Saturation(50); AssertImagesAreDifferent(original, imageFactory.Image, "because the saturation operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that a tint change is really applied by checking that the image is modified /// [Test] public void TestApplyEffectTint() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Tint(Color.FromKnownColor(KnownColor.AliceBlue)); AssertImagesAreDifferent(original, imageFactory.Image, "because the tint operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that a vignette change is really applied by checking that the image is modified /// [Test] public void TestApplyEffectVignette() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Vignette(Color.FromKnownColor(KnownColor.AliceBlue)); AssertImagesAreDifferent(original, imageFactory.Image, "because the vignette operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that a filter is really applied by checking that the image is modified /// [Test] public void TestApplyEffectWatermark() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Watermark(new TextLayer { FontFamily = new FontFamily("Arial"), FontSize = 10, Position = new Point(10, 10), Text = "Lorem ipsum dolor" }); AssertImagesAreDifferent(original, imageFactory.Image, "because the watermark operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that a filter is really applied by checking that the image is modified /// [Test] public void TestApplyEffectBlur() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.GaussianBlur(5); AssertImagesAreDifferent(original, imageFactory.Image, "because the blur operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that a filter is really applied by checking that the image is modified /// [Test] public void TestApplyEffectBlurWithLayer() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.GaussianBlur(new GaussianLayer { Sigma = 10, Size = 5, Threshold = 2 }); AssertImagesAreDifferent(original, imageFactory.Image, "because the layered blur operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that a filter is really applied by checking that the image is modified /// [Test] public void TestApplyEffectSharpen() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.GaussianSharpen(5); AssertImagesAreDifferent(original, imageFactory.Image, "because the sharpen operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that a filter is really applied by checking that the image is modified /// [Test] public void TestApplyEffectSharpenWithLayer() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.GaussianSharpen(new GaussianLayer { Sigma = 10, Size = 5, Threshold = 2 }); AssertImagesAreDifferent(original, imageFactory.Image, "because the layered sharpen operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that all filters can be applied /// [Test] public void TestApplyEffectFilter() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); List filters = new List() { MatrixFilters.BlackWhite, MatrixFilters.Comic, MatrixFilters.Gotham, MatrixFilters.GreyScale, MatrixFilters.HiSatch, MatrixFilters.Invert, MatrixFilters.Lomograph, MatrixFilters.LoSatch, MatrixFilters.Polaroid, MatrixFilters.Sepia }; foreach (IMatrixFilter filter in filters) { imageFactory.Filter(filter); AssertImagesAreDifferent(original, imageFactory.Image, "because the filter operation should have been applied on {0}", imageFactory.ImagePath); imageFactory.Reset(); AssertImagesAreIdentical(original, imageFactory.Image, "because the image should be reset"); } } } /// /// Tests that a filter is really applied by checking that the image is modified /// [Test] public void TestRoundedCorners() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.RoundedCorners(new RoundedCornerLayer(5)); AssertImagesAreDifferent(original, imageFactory.Image, "because the rounded corners operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that the image is well resized using constraints /// [Test] public void TestResizeConstraints() { const int MaxSize = 200; foreach (ImageFactory imageFactory in this.ListInputImages()) { imageFactory.Constrain(new Size(MaxSize, MaxSize)); imageFactory.Image.Width.Should().BeLessOrEqualTo(MaxSize, "because the image size should have been reduced"); imageFactory.Image.Height.Should().BeLessOrEqualTo(MaxSize, "because the image size should have been reduced"); } } /// /// Tests that the image is well cropped /// [Test] public void TestCrop() { const int MaxSize = 20; foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Crop(new Rectangle(0, 0, MaxSize, MaxSize)); AssertImagesAreDifferent(original, imageFactory.Image, "because the crop operation should have been applied on {0}", imageFactory.ImagePath); imageFactory.Image.Width.Should().Be(MaxSize, "because the cropped image should be {0}x{0}", MaxSize); imageFactory.Image.Height.Should().Be(MaxSize, "because the cropped image should be {0}x{0}", MaxSize); } } /// /// Tests that the image is well cropped /// [Test] public void TestCropWithCropLayer() { const int MaxSize = 20; foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Crop(new CropLayer(0, 0, MaxSize, MaxSize, CropMode.Pixels)); AssertImagesAreDifferent(original, imageFactory.Image, "because the layered crop operation should have been applied on {0}", imageFactory.ImagePath); imageFactory.Image.Width.Should().Be(MaxSize, "because the cropped image should be {0}x{0}", MaxSize); imageFactory.Image.Height.Should().Be(MaxSize, "because the cropped image should be {0}x{0}", MaxSize); } } /// /// Tests that the image is flipped /// [Test] public void TestFlip() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Flip(true); AssertImagesAreDifferent(original, imageFactory.Image, "because the vertical flip operation should have been applied on {0}", imageFactory.ImagePath); imageFactory.Image.Width.Should().Be(original.Width, "because the dimensions should not have changed"); imageFactory.Image.Height.Should().Be(original.Height, "because the dimensions should not have changed"); imageFactory.Reset(); AssertImagesAreIdentical(original, imageFactory.Image, "because the image should be reset"); imageFactory.Flip(); AssertImagesAreDifferent(original, imageFactory.Image, "because the horizontal flip operation should have been applied on {0}", imageFactory.ImagePath); imageFactory.Image.Width.Should().Be(original.Width, "because the dimensions should not have changed"); imageFactory.Image.Height.Should().Be(original.Height, "because the dimensions should not have changed"); } } /// /// Tests that the image is resized /// [Test] public void TestResize() { const int NewSize = 150; foreach (ImageFactory imageFactory in this.ListInputImages()) { imageFactory.Resize(new Size(NewSize, NewSize)); imageFactory.Image.Width.Should().Be(NewSize, "because the new image's size should be {0}x{0}", NewSize); imageFactory.Image.Height.Should().Be(NewSize, "because the new image's size should be {0}x{0}", NewSize); } } /// /// Tests that the image is resized /// [Test] public void TestResizeWithLayer() { const int NewSize = 150; foreach (ImageFactory imageFactory in this.ListInputImages()) { imageFactory.Resize(new ResizeLayer(new Size(NewSize, NewSize), ResizeMode.Stretch, AnchorPosition.Left)); imageFactory.Image.Width.Should().Be(NewSize, "because the new image's size should be {0}x{0}", NewSize); imageFactory.Image.Height.Should().Be(NewSize, "because the new image's size should be {0}x{0}", NewSize); } } /// /// Tests that the image is resized /// [Test] public void TestRotate() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Rotate(90); imageFactory.Image.Width.Should().Be(original.Height, "because the rotated image dimensions should have been switched"); imageFactory.Image.Height.Should().Be(original.Width, "because the rotated image dimensions should have been switched"); } } /// /// Tests that the images hue has been altered. /// [Test] public void TestHue() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Hue(90); AssertImagesAreDifferent(original, imageFactory.Image, "because the hue operation should have been applied on {0}", imageFactory.ImagePath); imageFactory.Reset(); AssertImagesAreIdentical(original, imageFactory.Image, "because the image should be reset"); imageFactory.Hue(116, true); AssertImagesAreDifferent(original, imageFactory.Image, "because the hue+rotate operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that the image has been pixelated. /// [Test] public void TestPixelate() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.Pixelate(8); AssertImagesAreDifferent(original, imageFactory.Image, "because the pixelate operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that the images quality has been set. /// [Test] public void TestQuality() { foreach (ImageFactory imageFactory in this.ListInputImages()) { int original = imageFactory.CurrentImageFormat.Quality; imageFactory.Quality(69); int updated = imageFactory.CurrentImageFormat.Quality; updated.Should().NotBe(original, "because the quality should have been changed"); } } /// /// Tests that the image has had a color replaced. /// [Test] public void TestReplaceColor() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); imageFactory.ReplaceColor(Color.White, Color.Black, 90); AssertImagesAreDifferent(original, imageFactory.Image, "because the color replace operation should have been applied on {0}", imageFactory.ImagePath); } } /// /// Tests that the various edge detection algorithms are applied. /// [Test] public void TestEdgeDetection() { foreach (ImageFactory imageFactory in this.ListInputImages()) { Image original = (Image)imageFactory.Image.Clone(); List filters = new List() { new KayyaliEdgeFilter(), new KirschEdgeFilter(), new Laplacian3X3EdgeFilter(), new Laplacian5X5EdgeFilter(), new LaplacianOfGaussianEdgeFilter(), new PrewittEdgeFilter(), new RobertsCrossEdgeFilter(), new ScharrEdgeFilter(), new SobelEdgeFilter() }; foreach (IEdgeFilter filter in filters) { imageFactory.DetectEdges(filter); AssertImagesAreDifferent(original, imageFactory.Image, "because the edge operation should have been applied on {0}", imageFactory.ImagePath); imageFactory.Reset(); AssertImagesAreIdentical(original, imageFactory.Image, "because the image should be reset"); } } } /// /// Gets the files matching the given extensions. /// /// /// The . /// /// /// The extensions. /// /// /// A collection of /// /// /// The extensions variable is null. /// private static IEnumerable GetFilesByExtensions(DirectoryInfo dir, params string[] extensions) { if (extensions == null) { throw new ArgumentNullException("extensions"); } IEnumerable files = dir.EnumerateFiles(); return files.Where(f => extensions.Contains(f.Extension, StringComparer.OrdinalIgnoreCase)); } /// /// Lists the input files in the Images folder /// /// The list of files. private IEnumerable ListInputFiles() { if (this.imagesInfos != null) { return this.imagesInfos; } DirectoryInfo directoryInfo = new DirectoryInfo("./Images"); this.imagesInfos = GetFilesByExtensions(directoryInfo, new[] { ".jpg", ".jpeg", ".png", ".gif", ".tiff", ".bmp", ".webp" }); return this.imagesInfos; } /// /// Lists the input images to use from the Images folder /// /// The list of images private IEnumerable ListInputImages() { if (imagesFactories == null || imagesFactories.Count() == 0) { imagesFactories = new List(); foreach (FileInfo fi in this.ListInputFiles()) { imagesFactories.Add((new ImageFactory()).Load(fi.FullName)); } } // reset all the images whenever we call this foreach (ImageFactory image in imagesFactories) { image.Reset(); } return imagesFactories; } /// /// Asserts that two images are identical /// /// The expected result /// The tested image private void AssertImagesAreIdentical(Image expected, Image tested, string because, params string[] becauseArgs) { ToByteArray(expected).SequenceEqual(ToByteArray(tested)).Should().BeTrue(because, becauseArgs); } /// /// Asserts that two images are different /// /// The not-expected result /// The tested image private void AssertImagesAreDifferent(Image expected, Image tested, string because, params string[] becauseArgs) { ToByteArray(expected).SequenceEqual(ToByteArray(tested)).Should().BeFalse(because, becauseArgs); } /// /// Converts an image to a byte array /// /// The image to convert /// The format to use /// An array of bytes representing the image public static byte[] ToByteArray(Image imageIn) { using (MemoryStream ms = new MemoryStream()) { imageIn.Save(ms, ImageFormat.Jpeg); return ms.ToArray(); } } } }