From c061df958422b527edb5de248ad26fdb73f33e6f Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 30 Aug 2020 13:04:03 +0200 Subject: [PATCH 1/5] Added unit tests for bokeh blur constructor --- .../Processors/Convolution/BokehBlurTest.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index 490a6ea49..50b8782e4 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -35,6 +35,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution 0.02565295+0.01611732j 0.0153483+0.01605112j 0.00698622+0.01370844j 0.00135338+0.00998296j -0.00152245+0.00604545j -0.00227282+0.002851j ]]"; + [Theory] + [InlineData(-10, 2, 3f)] + [InlineData(-1, 2, 3f)] + [InlineData(0, 2, 3f)] + [InlineData(20, -1, 3f)] + [InlineData(20, -0, 3f)] + [InlineData(20, 4, -10f)] + [InlineData(20, 4, 0f)] + public void VerifyBokehBlurProcessorArguments_Fail(int radius, int components, float gamma) + { + Assert.Throws(() => new BokehBlurProcessor(radius, components, gamma)); + } + [Fact] public void VerifyComplexComponents() { From 141abca768c45352a24b2d31bab7c37ba86079a3 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 30 Aug 2020 13:07:06 +0200 Subject: [PATCH 2/5] Added checks to bokeh blur constructor --- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 27eca523c..d329641e5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -47,6 +47,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public BokehBlurProcessor(int radius, int components, float gamma) { + Guard.MustBeGreaterThan(radius, 0, nameof(radius)); + Guard.MustBeBetweenOrEqualTo(components, 1, 6, nameof(components)); Guard.MustBeGreaterThanOrEqualTo(gamma, 1, nameof(gamma)); this.Radius = radius; From c69a41fe626e651298a0b56d107fef66d0aa9009 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 30 Aug 2020 13:07:36 +0200 Subject: [PATCH 3/5] Skipped checks in default bokeh blur constructor --- .../Processing/Processors/Convolution/BokehBlurProcessor.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index d329641e5..8a4c703e0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -29,8 +29,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// public BokehBlurProcessor() - : this(DefaultRadius, DefaultComponents, DefaultGamma) { + this.Radius = DefaultRadius; + this.Components = DefaultComponents; + this.Gamma = DefaultGamma; } /// From f0e8a0f6f502aa2c81352308cc5dd4d9d0aa3083 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 1 Sep 2020 00:23:53 +0100 Subject: [PATCH 4/5] Add missing SaveAsync method --- .../Advanced/AdvancedImageExtensions.cs | 4 +- src/ImageSharp/ImageExtensions.cs | 63 ++++++++++++++++--- .../Image/ImageTests.SaveAsync.cs | 31 +++++++++ 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index c5abbda61..54a773be0 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -23,7 +23,9 @@ namespace SixLabors.ImageSharp.Advanced /// /// The source image. /// The target file path to save the image to. - /// The matching encoder. + /// The file path is null. + /// No encoder available for provided path. + /// The matching . public static IImageEncoder DetectEncoder(this Image source, string filePath) { Guard.NotNull(filePath, nameof(filePath)); diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index d40c5c271..75cd32106 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -18,27 +18,29 @@ namespace SixLabors.ImageSharp public static partial class ImageExtensions { /// - /// Writes the image to the given stream using the currently loaded image format. + /// Writes the image to the given file path using an encoder detected from the path. /// /// The source image. /// The file path to save the image to. /// The path is null. + /// No encoder available for provided path. public static void Save(this Image source, string path) => source.Save(path, source.DetectEncoder(path)); /// - /// Writes the image to the given stream using the currently loaded image format. + /// Writes the image to the given file path using an encoder detected from the path. /// /// The source image. /// The file path to save the image to. /// The token to monitor for cancellation requests. /// The path is null. + /// No encoder available for provided path. /// A representing the asynchronous operation. public static Task SaveAsync(this Image source, string path, CancellationToken cancellationToken = default) => source.SaveAsync(path, source.DetectEncoder(path), cancellationToken); /// - /// Writes the image to the given stream using the currently loaded image format. + /// Writes the image to the given file path using the given image encoder. /// /// The source image. /// The file path to save the image to. @@ -56,7 +58,7 @@ namespace SixLabors.ImageSharp } /// - /// Writes the image to the given stream using the currently loaded image format. + /// Writes the image to the given file path using the given image encoder. /// /// The source image. /// The file path to save the image to. @@ -73,12 +75,15 @@ namespace SixLabors.ImageSharp { Guard.NotNull(path, nameof(path)); Guard.NotNull(encoder, nameof(encoder)); - using Stream fs = source.GetConfiguration().FileSystem.Create(path); - await source.SaveAsync(fs, encoder, cancellationToken).ConfigureAwait(false); + + using (Stream fs = source.GetConfiguration().FileSystem.Create(path)) + { + await source.SaveAsync(fs, encoder, cancellationToken).ConfigureAwait(false); + } } /// - /// Writes the image to the given stream using the currently loaded image format. + /// Writes the image to the given stream using the given image format. /// /// The source image. /// The stream to save the image to. @@ -115,6 +120,50 @@ namespace SixLabors.ImageSharp source.Save(stream, encoder); } + /// + /// Writes the image to the given stream using the given image format. + /// + /// The source image. + /// The stream to save the image to. + /// The format to save the image in. + /// The token to monitor for cancellation requests. + /// The stream is null. + /// The format is null. + /// The stream is not writable. + /// No encoder available for provided format. + /// A representing the asynchronous operation. + public static Task SaveAsync( + this Image source, + Stream stream, + IImageFormat format, + CancellationToken cancellationToken = default) + { + Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(format, nameof(format)); + + if (!stream.CanWrite) + { + throw new NotSupportedException("Cannot write to the stream."); + } + + IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format); + + if (encoder is null) + { + var sb = new StringBuilder(); + sb.AppendLine("No encoder was found for the provided mime type. Registered encoders include:"); + + foreach (KeyValuePair val in source.GetConfiguration().ImageFormatsManager.ImageEncoders) + { + sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine); + } + + throw new NotSupportedException(sb.ToString()); + } + + return source.SaveAsync(stream, encoder, cancellationToken); + } + /// /// Returns a Base64 encoded string from the given image. /// The result is prepended with a Data URI diff --git a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs index 40c3b65b5..4e6b002d0 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs @@ -72,6 +72,37 @@ namespace SixLabors.ImageSharp.Tests } } + [Theory] + [InlineData("test.png", "image/png")] + [InlineData("test.tga", "image/tga")] + [InlineData("test.bmp", "image/bmp")] + [InlineData("test.jpg", "image/jpeg")] + [InlineData("test.gif", "image/gif")] + public async Task SaveStreamWithMime(string filename, string mimeType) + { + using (var image = new Image(5, 5)) + { + string ext = Path.GetExtension(filename); + IImageFormat format = image.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext); + Assert.Equal(mimeType, format.DefaultMimeType); + + using (var stream = new MemoryStream()) + { + var asyncStream = new AsyncStreamWrapper(stream, () => false); + await image.SaveAsync(asyncStream, format); + + stream.Position = 0; + + (Image Image, IImageFormat Format) imf = await Image.LoadWithFormatAsync(stream); + + Assert.Equal(format, imf.Format); + Assert.Equal(mimeType, imf.Format.DefaultMimeType); + + imf.Image.Dispose(); + } + } + } + [Fact] public async Task ThrowsWhenDisposed() { From 1aa26a0e17709f7a6d51f07c2fb5c87b85445fed Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 1 Sep 2020 07:48:17 +0100 Subject: [PATCH 5/5] Fix Codecov --- .github/workflows/build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 412b1d807..0e093a834 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -64,7 +64,7 @@ jobs: XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit - name: Update Codecov - uses: codecov/codecov-action@v1.0.7 + uses: codecov/codecov-action@v1 if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors') with: flags: unittests