From a1b84211ad354322b464bd8798ceb535522961d3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 14 Feb 2023 19:42:41 +1000 Subject: [PATCH 01/34] Update to ImageSharp v3 builds --- .gitattributes | 5 +- samples/ImageSharp.Web.Sample/Startup.cs | 92 ++++++++----------- shared-infrastructure | 2 +- src/ImageSharp.Web/FormatUtilities.cs | 6 +- src/ImageSharp.Web/FormattedImage.cs | 27 +++--- src/ImageSharp.Web/ImageSharp.Web.csproj | 2 +- .../Middleware/ImageSharpMiddleware.cs | 7 +- .../Middleware/ImageSharpMiddlewareOptions.cs | 5 +- .../Processors/FormatWebProcessor.cs | 10 +- .../Processors/QualityWebProcessor.cs | 8 +- .../Processors/AutoOrientWebProcessorTests.cs | 8 +- .../Processors/FormattedImageTests.cs | 22 ++--- .../TestUtilities/ServerTestBase.cs | 16 ++-- 13 files changed, 96 insertions(+), 114 deletions(-) diff --git a/.gitattributes b/.gitattributes index 2fdea90e..ff4ec940 100644 --- a/.gitattributes +++ b/.gitattributes @@ -64,18 +64,19 @@ # Set explicit file behavior to: # treat as text # normalize to Unix-style line endings and -# use a union merge when resoling conflicts +# use a union merge when resolving conflicts ############################################################################### *.csproj text eol=lf merge=union *.dbproj text eol=lf merge=union *.fsproj text eol=lf merge=union *.ncrunchproject text eol=lf merge=union *.vbproj text eol=lf merge=union +*.shproj text eol=lf merge=union ############################################################################### # Set explicit file behavior to: # treat as text # normalize to Windows-style line endings and -# use a union merge when resoling conflicts +# use a union merge when resolving conflicts ############################################################################### *.sln text eol=crlf merge=union ############################################################################### diff --git a/samples/ImageSharp.Web.Sample/Startup.cs b/samples/ImageSharp.Web.Sample/Startup.cs index eaf01e31..63ac57eb 100644 --- a/samples/ImageSharp.Web.Sample/Startup.cs +++ b/samples/ImageSharp.Web.Sample/Startup.cs @@ -30,52 +30,46 @@ public class Startup /// /// The collection of service descriptors. public void ConfigureServices(IServiceCollection services) - { - services.AddImageSharp() - .SetRequestParser() - .Configure(options => - { - options.CacheRootPath = null; - options.CacheFolder = "is-cache"; - options.CacheFolderDepth = 8; - }) - .SetCache() - .SetCacheKey() - .SetCacheHash() - .Configure(options => - { - options.ProviderRootPath = null; - }) - .AddProvider() - .AddProcessor() - .AddProcessor() - .AddProcessor() - .AddProcessor() - .AddProcessor(); + => services.AddImageSharp() + .SetRequestParser() + .Configure(options => + { + options.CacheRootPath = null; + options.CacheFolder = "is-cache"; + options.CacheFolderDepth = 8; + }) + .SetCache() + .SetCacheKey() + .SetCacheHash() + .Configure(options => options.ProviderRootPath = null) + .AddProvider() + .AddProcessor() + .AddProcessor() + .AddProcessor() + .AddProcessor() + .AddProcessor(); - // Add the default service and options. - // - // services.AddImageSharp(); + // Add the default service and options. + // + // services.AddImageSharp(); - // Or add the default service and custom options. - // - // this.ConfigureDefaultServicesAndCustomOptions(services); + // Or add the default service and custom options. + // + // this.ConfigureDefaultServicesAndCustomOptions(services); - // Or we can fine-grain control adding the default options and configure all other services. - // - // this.ConfigureCustomServicesAndDefaultOptions(services); + // Or we can fine-grain control adding the default options and configure all other services. + // + // this.ConfigureCustomServicesAndDefaultOptions(services); - // Or we can fine-grain control adding custom options and configure all other services - // There are also factory methods for each builder that will allow building from configuration files. - // - // this.ConfigureCustomServicesAndCustomOptions(services); - } + // Or we can fine-grain control adding custom options and configure all other services + // There are also factory methods for each builder that will allow building from configuration files. + // + // this.ConfigureCustomServicesAndCustomOptions(services); private void ConfigureDefaultServicesAndCustomOptions(IServiceCollection services) - { - services.AddImageSharp(options => + => services.AddImageSharp(options => { - options.Configuration = Configuration.Default; + options.DecoderOptions = new(); options.BrowserMaxAge = TimeSpan.FromDays(7); options.CacheMaxAge = TimeSpan.FromDays(365); options.CacheHashLength = 8; @@ -84,20 +78,16 @@ private void ConfigureDefaultServicesAndCustomOptions(IServiceCollection service options.OnProcessedAsync = _ => Task.CompletedTask; options.OnPrepareResponseAsync = _ => Task.CompletedTask; }); - } private void ConfigureCustomServicesAndDefaultOptions(IServiceCollection services) - { - services.AddImageSharp() - .RemoveProcessor() - .RemoveProcessor(); - } + => services.AddImageSharp() + .RemoveProcessor() + .RemoveProcessor(); private void ConfigureCustomServicesAndCustomOptions(IServiceCollection services) - { - services.AddImageSharp(options => + => services.AddImageSharp(options => { - options.Configuration = Configuration.Default; + options.DecoderOptions = new(); options.BrowserMaxAge = TimeSpan.FromDays(7); options.CacheMaxAge = TimeSpan.FromDays(365); options.CacheHashLength = 8; @@ -107,10 +97,7 @@ private void ConfigureCustomServicesAndCustomOptions(IServiceCollection services options.OnPrepareResponseAsync = _ => Task.CompletedTask; }) .SetRequestParser() - .Configure(options => - { - options.CacheFolder = "different-cache"; - }) + .Configure(options => options.CacheFolder = "different-cache") .SetCache() .SetCacheKey() .SetCacheHash() @@ -121,7 +108,6 @@ private void ConfigureCustomServicesAndCustomOptions(IServiceCollection services .AddProcessor() .AddProcessor() .AddProcessor(); - } /// /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/shared-infrastructure b/shared-infrastructure index 6c52486c..777842e3 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 6c52486c512357475cbb92bbb7c4c0af4d85b1db +Subproject commit 777842e38622ab374e03f4076cf2544ed54a062b diff --git a/src/ImageSharp.Web/FormatUtilities.cs b/src/ImageSharp.Web/FormatUtilities.cs index 174950b8..498d05f8 100644 --- a/src/ImageSharp.Web/FormatUtilities.cs +++ b/src/ImageSharp.Web/FormatUtilities.cs @@ -28,7 +28,7 @@ public FormatUtilities(IOptions options) { Guard.NotNull(options, nameof(options)); - foreach (IImageFormat imageFormat in options.Value.Configuration.ImageFormats) + foreach (IImageFormat imageFormat in options.Value.DecoderOptions.Configuration.ImageFormats) { string[] extensions = imageFormat.FileExtensions.ToArray(); @@ -60,7 +60,7 @@ public bool TryGetExtensionFromUri(string uri, out string extension) if (query > -1) { if (uri.Contains(FormatWebProcessor.Format, StringComparison.OrdinalIgnoreCase) - && QueryHelpers.ParseQuery(uri.Substring(query)).TryGetValue(FormatWebProcessor.Format, out StringValues ext)) + && QueryHelpers.ParseQuery(uri[query..]).TryGetValue(FormatWebProcessor.Format, out StringValues ext)) { // We have a query but is it a valid one? ReadOnlySpan extSpan = ext[0].AsSpan(); @@ -86,7 +86,7 @@ public bool TryGetExtensionFromUri(string uri, out string extension) int extensionIndex; if ((extensionIndex = path.LastIndexOf('.')) != -1) { - ReadOnlySpan pathExtension = path.Slice(extensionIndex + 1); + ReadOnlySpan pathExtension = path[(extensionIndex + 1)..]; foreach (string e in this.extensions) { diff --git a/src/ImageSharp.Web/FormattedImage.cs b/src/ImageSharp.Web/FormattedImage.cs index 4a270918..0ea5abbb 100644 --- a/src/ImageSharp.Web/FormattedImage.cs +++ b/src/ImageSharp.Web/FormattedImage.cs @@ -6,7 +6,6 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Web; @@ -64,7 +63,7 @@ public IImageFormat Format } this.format = value; - this.encoder = this.imageFormatsManager.FindEncoder(value); + this.encoder = this.imageFormatsManager.GetEncoder(value); } } @@ -82,7 +81,7 @@ public IImageEncoder Encoder } // The given type should match the format encoder. - IImageEncoder reference = this.imageFormatsManager.FindEncoder(this.Format); + IImageEncoder reference = this.imageFormatsManager.GetEncoder(this.Format); if (reference.GetType() != value.GetType()) { ThrowInvalid(nameof(value)); @@ -96,26 +95,29 @@ public IImageEncoder Encoder /// Create a new instance of the class from the given stream. /// /// The pixel format. - /// The configuration. + /// The general decoder options. /// The source. /// A representing the asynchronous operation. - internal static async Task LoadAsync(Configuration configuration, Stream source) + internal static async Task LoadAsync(DecoderOptions options, Stream source) where TPixel : unmanaged, IPixel { - (Image image, IImageFormat format) = await Image.LoadWithFormatAsync(configuration, source); - return new FormattedImage(image, format, false); + // TODO: We want to be able to apply decoder options per request. + // For example. If a resize command has been passed with no extra resampling options + // then we should apply those changes on decode. This will allow memory savings and performance improvements. + Image image = await Image.LoadAsync(options, source); + return new FormattedImage(image, image.Metadata.DecodedImageFormat, false); } /// /// Create a new instance of the class from the given stream. /// - /// The configuration. + /// The general decoder options. /// The source. /// A representing the asynchronous operation. - internal static async Task LoadAsync(Configuration configuration, Stream source) + internal static async Task LoadAsync(DecoderOptions options, Stream source) { - (Image image, IImageFormat format) = await Image.LoadWithFormatAsync(configuration, source); - return new FormattedImage(image, format, false); + Image image = await Image.LoadAsync(options, source); + return new FormattedImage(image, image.Metadata.DecodedImageFormat, false); } /// @@ -141,8 +143,7 @@ public bool TryGetExifOrientation(out ushort value) value = ExifOrientationMode.Unknown; if (this.Image.Metadata.ExifProfile != null) { - IExifValue orientation = this.Image.Metadata.ExifProfile.GetValue(ExifTag.Orientation); - if (orientation is null) + if (!this.Image.Metadata.ExifProfile.TryGetValue(ExifTag.Orientation, out IExifValue orientation)) { return false; } diff --git a/src/ImageSharp.Web/ImageSharp.Web.csproj b/src/ImageSharp.Web/ImageSharp.Web.csproj index 26986650..17866c44 100644 --- a/src/ImageSharp.Web/ImageSharp.Web.csproj +++ b/src/ImageSharp.Web/ImageSharp.Web.csproj @@ -45,7 +45,7 @@ - + diff --git a/src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs b/src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs index d964f218..a4686ddd 100644 --- a/src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs +++ b/src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs @@ -10,7 +10,6 @@ using Microsoft.Extensions.Options; using Microsoft.IO; using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Web.Caching; using SixLabors.ImageSharp.Web.Commands; using SixLabors.ImageSharp.Web.Processors; @@ -352,7 +351,7 @@ private async Task ProcessRequestAsync( { await inStream.CopyToAsync(outStream); outStream.Position = 0; - format = await Image.DetectFormatAsync(this.options.Configuration, outStream); + format = await Image.DetectFormatAsync(this.options.DecoderOptions, outStream); } else { @@ -370,11 +369,11 @@ private async Task ProcessRequestAsync( if (requiresAlpha) { - image = await FormattedImage.LoadAsync(this.options.Configuration, inStream); + image = await FormattedImage.LoadAsync(this.options.DecoderOptions, inStream); } else { - image = await FormattedImage.LoadAsync(this.options.Configuration, inStream); + image = await FormattedImage.LoadAsync(this.options.DecoderOptions, inStream); } image.Process( diff --git a/src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs b/src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs index ebb294e8..3a14b42f 100644 --- a/src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs +++ b/src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs @@ -5,6 +5,7 @@ using System.Globalization; using Microsoft.AspNetCore.Http; using Microsoft.IO; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Web.Commands; using SixLabors.ImageSharp.Web.Providers; @@ -32,9 +33,9 @@ public class ImageSharpMiddlewareOptions private Func onPrepareResponseAsync = _ => Task.CompletedTask; /// - /// Gets or sets the base library configuration. + /// Gets or sets the default decoder options. /// - public Configuration Configuration { get; set; } = Configuration.Default; + public DecoderOptions DecoderOptions { get; set; } = new(); /// /// Gets or sets the recyclable memorystream manager used for managing pooled stream diff --git a/src/ImageSharp.Web/Processors/FormatWebProcessor.cs b/src/ImageSharp.Web/Processors/FormatWebProcessor.cs index f8665dc4..a11bc438 100644 --- a/src/ImageSharp.Web/Processors/FormatWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/FormatWebProcessor.cs @@ -52,14 +52,10 @@ public FormattedImage Process( { string extension = commands.GetValueOrDefault(Format); - if (!string.IsNullOrWhiteSpace(extension)) + if (!string.IsNullOrWhiteSpace(extension) + && this.options.DecoderOptions.Configuration.ImageFormatsManager.TryFindFormatByFileExtension(extension, out IImageFormat format)) { - IImageFormat format = this.options.Configuration.ImageFormatsManager.FindFormatByFileExtension(extension); - - if (format != null) - { - image.Format = format; - } + image.Format = format; } return image; diff --git a/src/ImageSharp.Web/Processors/QualityWebProcessor.cs b/src/ImageSharp.Web/Processors/QualityWebProcessor.cs index 76af983f..81a3b8fb 100644 --- a/src/ImageSharp.Web/Processors/QualityWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/QualityWebProcessor.cs @@ -45,11 +45,11 @@ public FormattedImage Process( if (image.Format is JpegFormat) { - var reference = + JpegEncoder reference = (JpegEncoder)image.Image .GetConfiguration() .ImageFormatsManager - .FindEncoder(image.Format); + .GetEncoder(image.Format); if (quality != reference.Quality) { @@ -58,11 +58,11 @@ public FormattedImage Process( } else if (image.Format is WebpFormat) { - var reference = + WebpEncoder reference = (WebpEncoder)image.Image .GetConfiguration() .ImageFormatsManager - .FindEncoder(image.Format); + .GetEncoder(image.Format); image.Encoder = new WebpEncoder() { diff --git a/tests/ImageSharp.Web.Tests/Processors/AutoOrientWebProcessorTests.cs b/tests/ImageSharp.Web.Tests/Processors/AutoOrientWebProcessorTests.cs index de4b8919..706c8f31 100644 --- a/tests/ImageSharp.Web.Tests/Processors/AutoOrientWebProcessorTests.cs +++ b/tests/ImageSharp.Web.Tests/Processors/AutoOrientWebProcessorTests.cs @@ -26,17 +26,17 @@ public void AutoOrientWebProcessor_UpdatesOrientation() const ushort tl = 1; const ushort br = 3; - using var image = new Image(1, 1); + using Image image = new(1, 1); image.Metadata.ExifProfile = new(); image.Metadata.ExifProfile.SetValue(ExifTag.Orientation, br); - IExifValue orientation = image.Metadata.ExifProfile.GetValue(ExifTag.Orientation); + Assert.True(image.Metadata.ExifProfile.TryGetValue(ExifTag.Orientation, out IExifValue orientation)); Assert.Equal(br, orientation.Value); - using var formatted = new FormattedImage(image, PngFormat.Instance); + using FormattedImage formatted = new(image, PngFormat.Instance); new AutoOrientWebProcessor().Process(formatted, null, commands, parser, culture); - orientation = image.Metadata.ExifProfile.GetValue(ExifTag.Orientation); + Assert.True(image.Metadata.ExifProfile.TryGetValue(ExifTag.Orientation, out orientation)); Assert.Equal(tl, orientation.Value); } } diff --git a/tests/ImageSharp.Web.Tests/Processors/FormattedImageTests.cs b/tests/ImageSharp.Web.Tests/Processors/FormattedImageTests.cs index 66ed89eb..982fb5ea 100644 --- a/tests/ImageSharp.Web.Tests/Processors/FormattedImageTests.cs +++ b/tests/ImageSharp.Web.Tests/Processors/FormattedImageTests.cs @@ -12,8 +12,8 @@ public class FormattedImageTests [Fact] public void ConstructorSetsProperties() { - using var image = new Image(1, 1); - using var formatted = new FormattedImage(image, JpegFormat.Instance); + using Image image = new(1, 1); + using FormattedImage formatted = new(image, JpegFormat.Instance); Assert.NotNull(formatted.Image); Assert.Equal(image, formatted.Image); @@ -28,8 +28,8 @@ public void ConstructorSetsProperties() [Fact] public void CanSetFormat() { - using var image = new Image(1, 1); - using var formatted = new FormattedImage(image, JpegFormat.Instance); + using Image image = new(1, 1); + using FormattedImage formatted = new(image, JpegFormat.Instance); Assert.NotNull(formatted.Format); Assert.Equal(JpegFormat.Instance, formatted.Format); @@ -44,8 +44,8 @@ public void CanSetFormat() [Fact] public void CanSetEncoder() { - using var image = new Image(1, 1); - using var formatted = new FormattedImage(image, PngFormat.Instance); + using Image image = new(1, 1); + using FormattedImage formatted = new(image, PngFormat.Instance); Assert.NotNull(formatted.Format); Assert.Equal(PngFormat.Instance, formatted.Format); @@ -56,14 +56,14 @@ public void CanSetEncoder() formatted.Format = JpegFormat.Instance; Assert.Equal(typeof(JpegEncoder), formatted.Encoder.GetType()); - JpegColorType current = ((JpegEncoder)formatted.Encoder).ColorType.GetValueOrDefault(); + JpegEncodingColor current = ((JpegEncoder)formatted.Encoder).ColorType.GetValueOrDefault(); - Assert.Equal(JpegColorType.YCbCrRatio420, current); - formatted.Encoder = new JpegEncoder { ColorType = JpegColorType.YCbCrRatio444 }; + Assert.Equal(JpegEncodingColor.YCbCrRatio420, current); + formatted.Encoder = new JpegEncoder { ColorType = JpegEncodingColor.YCbCrRatio444 }; - JpegColorType replacement = ((JpegEncoder)formatted.Encoder).ColorType.GetValueOrDefault(); + JpegEncodingColor replacement = ((JpegEncoder)formatted.Encoder).ColorType.GetValueOrDefault(); Assert.NotEqual(current, replacement); - Assert.Equal(JpegColorType.YCbCrRatio444, replacement); + Assert.Equal(JpegEncodingColor.YCbCrRatio444, replacement); } } diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs b/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs index f9fa82a2..6d8b5e83 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs @@ -42,7 +42,7 @@ public async Task CanProcessAndResolveImageAsync() string url = this.ImageSource; string ext = Path.GetExtension(url); - IImageFormat format = Configuration.Default.ImageFormatsManager.FindFormatByFileExtension(ext); + Assert.True(Configuration.Default.ImageFormatsManager.TryFindFormatByFileExtension(ext, out IImageFormat format)); // First response HttpResponseMessage response = await this.HttpClient.GetAsync(url + await this.AugmentCommandAsync(this.Fixture.Commands[0])); @@ -52,11 +52,10 @@ public async Task CanProcessAndResolveImageAsync() Assert.True(response.Content.Headers.ContentLength > 0); Assert.Equal(format.DefaultMimeType, response.Content.Headers.ContentType.MediaType); - (Image Image, IImageFormat Format) actual = await Image.LoadWithFormatAsync(await response.Content.ReadAsStreamAsync()); - using Image image = actual.Image; + using Image image = await Image.LoadAsync(await response.Content.ReadAsStreamAsync()); Assert.Equal(Width, image.Width); - Assert.Equal(format, actual.Format); + Assert.Equal(format, image.Metadata.DecodedImageFormat); response.Dispose(); @@ -68,16 +67,15 @@ public async Task CanProcessAndResolveImageAsync() Assert.True(response.Content.Headers.ContentLength > 0); Assert.Equal(format.DefaultMimeType, response.Content.Headers.ContentType.MediaType); - (Image Image, IImageFormat Format) cachedActual = await Image.LoadWithFormatAsync(await response.Content.ReadAsStreamAsync()); - using Image cached = cachedActual.Image; + using Image cached = await Image.LoadAsync(await response.Content.ReadAsStreamAsync()); Assert.Equal(Width, cached.Width); - Assert.Equal(format, actual.Format); + Assert.Equal(format, image.Metadata.DecodedImageFormat); response.Dispose(); // 304 response - var request = new HttpRequestMessage + HttpRequestMessage request = new() { RequestUri = new Uri(url + await this.AugmentCommandAsync(this.Fixture.Commands[0])), Method = HttpMethod.Get, @@ -133,7 +131,7 @@ public async Task CanProcessMultipleIdenticalQueriesAsync() Assert.True(response.Content.Headers.ContentLength > 0); })).ToArray(); - var all = Task.WhenAll(tasks); + Task all = Task.WhenAll(tasks); await all; Assert.True(all.IsCompletedSuccessfully); } From 209298f6be0951d73cf55d45f3b2f8907c7a855e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 14 Feb 2023 19:50:04 +1000 Subject: [PATCH 02/34] Remove unused namespaces --- src/ImageSharp.Web/ExifOrientationUtilities.cs | 1 - src/ImageSharp.Web/Processors/AutoOrientWebProcessor.cs | 1 - src/ImageSharp.Web/Processors/BackgroundColorWebProcessor.cs | 1 - src/ImageSharp.Web/Processors/ResizeWebProcessor.cs | 1 - 4 files changed, 4 deletions(-) diff --git a/src/ImageSharp.Web/ExifOrientationUtilities.cs b/src/ImageSharp.Web/ExifOrientationUtilities.cs index 15e0d336..2e94f50a 100644 --- a/src/ImageSharp.Web/ExifOrientationUtilities.cs +++ b/src/ImageSharp.Web/ExifOrientationUtilities.cs @@ -5,7 +5,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp.Web; diff --git a/src/ImageSharp.Web/Processors/AutoOrientWebProcessor.cs b/src/ImageSharp.Web/Processors/AutoOrientWebProcessor.cs index 392d58a5..9c3c4867 100644 --- a/src/ImageSharp.Web/Processors/AutoOrientWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/AutoOrientWebProcessor.cs @@ -4,7 +4,6 @@ using System.Globalization; using Microsoft.Extensions.Logging; -using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Web.Commands; namespace SixLabors.ImageSharp.Web.Processors; diff --git a/src/ImageSharp.Web/Processors/BackgroundColorWebProcessor.cs b/src/ImageSharp.Web/Processors/BackgroundColorWebProcessor.cs index 7fd82474..79668e09 100644 --- a/src/ImageSharp.Web/Processors/BackgroundColorWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/BackgroundColorWebProcessor.cs @@ -4,7 +4,6 @@ using System.Globalization; using Microsoft.Extensions.Logging; -using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Web.Commands; namespace SixLabors.ImageSharp.Web.Processors; diff --git a/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs b/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs index 37a53a67..d4c1b2de 100644 --- a/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs @@ -6,7 +6,6 @@ using System.Numerics; using Microsoft.Extensions.Logging; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Web.Commands; From 581b9e0c7083d1ad78bc4a62e4375c257fc36091 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 Feb 2023 20:34:31 +1000 Subject: [PATCH 03/34] Update ImageSharp and fix build --- ci-pack.ps1 | 2 +- samples/ImageSharp.Web.Sample/Startup.cs | 4 ++-- src/ImageSharp.Web/FormatUtilities.cs | 2 +- src/ImageSharp.Web/ImageSharp.Web.csproj | 2 +- .../Middleware/ImageSharpMiddleware.cs | 11 +++++++--- .../Middleware/ImageSharpMiddlewareOptions.cs | 20 +++++++++++++++++-- .../Processors/FormatWebProcessor.cs | 2 +- 7 files changed, 32 insertions(+), 11 deletions(-) diff --git a/ci-pack.ps1 b/ci-pack.ps1 index 09f45347..55c69fb5 100644 --- a/ci-pack.ps1 +++ b/ci-pack.ps1 @@ -3,4 +3,4 @@ dotnet clean -c Release $repositoryUrl = "https://github.com/$env:GITHUB_REPOSITORY" # Building for packing and publishing. -dotnet pack -c Release --output "$PSScriptRoot/artifacts" /p:RepositoryUrl=$repositoryUrl +dotnet pack -c Release -p:PackageOutputPath="$PSScriptRoot/artifacts" -p:RepositoryUrl=$repositoryUrl diff --git a/samples/ImageSharp.Web.Sample/Startup.cs b/samples/ImageSharp.Web.Sample/Startup.cs index 63ac57eb..a01e8c07 100644 --- a/samples/ImageSharp.Web.Sample/Startup.cs +++ b/samples/ImageSharp.Web.Sample/Startup.cs @@ -69,7 +69,7 @@ public void ConfigureServices(IServiceCollection services) private void ConfigureDefaultServicesAndCustomOptions(IServiceCollection services) => services.AddImageSharp(options => { - options.DecoderOptions = new(); + options.Configuration = Configuration.Default; options.BrowserMaxAge = TimeSpan.FromDays(7); options.CacheMaxAge = TimeSpan.FromDays(365); options.CacheHashLength = 8; @@ -87,7 +87,7 @@ private void ConfigureCustomServicesAndDefaultOptions(IServiceCollection service private void ConfigureCustomServicesAndCustomOptions(IServiceCollection services) => services.AddImageSharp(options => { - options.DecoderOptions = new(); + options.Configuration = Configuration.Default; options.BrowserMaxAge = TimeSpan.FromDays(7); options.CacheMaxAge = TimeSpan.FromDays(365); options.CacheHashLength = 8; diff --git a/src/ImageSharp.Web/FormatUtilities.cs b/src/ImageSharp.Web/FormatUtilities.cs index 498d05f8..2d7da4ea 100644 --- a/src/ImageSharp.Web/FormatUtilities.cs +++ b/src/ImageSharp.Web/FormatUtilities.cs @@ -28,7 +28,7 @@ public FormatUtilities(IOptions options) { Guard.NotNull(options, nameof(options)); - foreach (IImageFormat imageFormat in options.Value.DecoderOptions.Configuration.ImageFormats) + foreach (IImageFormat imageFormat in options.Value.Configuration.ImageFormats) { string[] extensions = imageFormat.FileExtensions.ToArray(); diff --git a/src/ImageSharp.Web/ImageSharp.Web.csproj b/src/ImageSharp.Web/ImageSharp.Web.csproj index 17866c44..87b6d8cb 100644 --- a/src/ImageSharp.Web/ImageSharp.Web.csproj +++ b/src/ImageSharp.Web/ImageSharp.Web.csproj @@ -45,7 +45,7 @@ - + diff --git a/src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs b/src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs index a4686ddd..02424756 100644 --- a/src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs +++ b/src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs @@ -346,12 +346,17 @@ private async Task ProcessRequestAsync( using (Stream inStream = await sourceImageResolver.OpenReadAsync()) { + DecoderOptions decoderOptions = new() { Configuration = this.options.Configuration }; + + // TODO: We need some way to set options based upon processors. + await this.options.OnBeforeLoadAsync.Invoke(httpContext, decoderOptions); + // No commands? We simply copy the stream across. if (commands.Count == 0) { await inStream.CopyToAsync(outStream); outStream.Position = 0; - format = await Image.DetectFormatAsync(this.options.DecoderOptions, outStream); + format = await Image.DetectFormatAsync(decoderOptions, outStream); } else { @@ -369,11 +374,11 @@ private async Task ProcessRequestAsync( if (requiresAlpha) { - image = await FormattedImage.LoadAsync(this.options.DecoderOptions, inStream); + image = await FormattedImage.LoadAsync(decoderOptions, inStream); } else { - image = await FormattedImage.LoadAsync(this.options.DecoderOptions, inStream); + image = await FormattedImage.LoadAsync(decoderOptions, inStream); } image.Process( diff --git a/src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs b/src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs index 3a14b42f..6c7bfd6e 100644 --- a/src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs +++ b/src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs @@ -28,14 +28,15 @@ public class ImageSharpMiddlewareOptions }; private Func onParseCommandsAsync = _ => Task.CompletedTask; + private Func onBeforeLoadAsync = (_, _) => Task.CompletedTask; private Func onBeforeSaveAsync = _ => Task.CompletedTask; private Func onProcessedAsync = _ => Task.CompletedTask; private Func onPrepareResponseAsync = _ => Task.CompletedTask; /// - /// Gets or sets the default decoder options. + /// Gets or sets the base library configuration. /// - public DecoderOptions DecoderOptions { get; set; } = new(); + public Configuration Configuration { get; set; } = Configuration.Default; /// /// Gets or sets the recyclable memorystream manager used for managing pooled stream @@ -114,6 +115,21 @@ public Func OnParseCommandsAsync } } + /// + /// Gets or sets the method that can be used to used to augment decoder options. + /// This is called before the image is decoded and loaded for processing. + /// + public Func OnBeforeLoadAsync + { + get => this.onBeforeLoadAsync; + + set + { + Guard.NotNull(value, nameof(this.OnBeforeLoadAsync)); + this.onBeforeLoadAsync = value; + } + } + /// /// Gets or sets the additional method that can be used for final manipulation before the image is saved. /// This is called after image has been processed, but before the image has been saved to the output stream for caching. diff --git a/src/ImageSharp.Web/Processors/FormatWebProcessor.cs b/src/ImageSharp.Web/Processors/FormatWebProcessor.cs index a11bc438..0117e9b6 100644 --- a/src/ImageSharp.Web/Processors/FormatWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/FormatWebProcessor.cs @@ -53,7 +53,7 @@ public FormattedImage Process( string extension = commands.GetValueOrDefault(Format); if (!string.IsNullOrWhiteSpace(extension) - && this.options.DecoderOptions.Configuration.ImageFormatsManager.TryFindFormatByFileExtension(extension, out IImageFormat format)) + && this.options.Configuration.ImageFormatsManager.TryFindFormatByFileExtension(extension, out IImageFormat format)) { image.Format = format; } From 9e835f4d09245107c3c838947ad1d0b4eff02378 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 Feb 2023 20:51:49 +1000 Subject: [PATCH 04/34] Update TestServerFixture.cs --- tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs b/tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs index 12ed053d..09a187cd 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs @@ -21,7 +21,9 @@ public abstract class TestServerFixture : IDisposable private TestServer server; private bool isDisposed; - protected TestServerFixture() +#pragma warning disable RCS1160 // Abstract type should not have public constructors. + public TestServerFixture() +#pragma warning restore RCS1160 // Abstract type should not have public constructors. { IConfigurationRoot configuration = new ConfigurationBuilder() .AddInMemoryCollection(new[] From edb5b5bb468d9ecab3fed1ba4999d693578cd71c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 Feb 2023 21:23:26 +1000 Subject: [PATCH 05/34] Try installing and running the s3ver locally --- .github/workflows/build-and-test.yml | 8 ++++---- src/ImageSharp.Web.Providers.AWS/AmazonS3ClientFactory.cs | 5 +++-- .../TestUtilities/AWSS3StorageImageProviderFactory.cs | 8 ++++---- .../TestUtilities/TestServerFixture.cs | 4 +--- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 97aafb97..06f64fdb 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -101,15 +101,15 @@ jobs: if: matrix.options.os != 'windows-latest' shell: bash run: | - sudo npm install -g s3rver - sudo s3rver -d . & + sudo npm install s3rver + sudo npx s3rver -d . & - name: S3rver Setup Windows if: matrix.options.os == 'windows-latest' shell: bash run: | - npm install -g s3rver - s3rver -d . & + npm install s3rver + npx s3rver -d . & - name: DotNet Setup uses: actions/setup-dotnet@v3 diff --git a/src/ImageSharp.Web.Providers.AWS/AmazonS3ClientFactory.cs b/src/ImageSharp.Web.Providers.AWS/AmazonS3ClientFactory.cs index 772cc42d..e311ae86 100644 --- a/src/ImageSharp.Web.Providers.AWS/AmazonS3ClientFactory.cs +++ b/src/ImageSharp.Web.Providers.AWS/AmazonS3ClientFactory.cs @@ -18,6 +18,7 @@ internal static class AmazonS3ClientFactory /// /// A new . /// + /// Invalid configuration. public static AmazonS3Client CreateClient(IAWSS3BucketClientOptions options) { if (!string.IsNullOrWhiteSpace(options.Endpoint)) @@ -33,14 +34,14 @@ public static AmazonS3Client CreateClient(IAWSS3BucketClientOptions options) { // AccessSecret can be empty. Guard.NotNullOrWhiteSpace(options.Region, nameof(options.Region)); - var region = RegionEndpoint.GetBySystemName(options.Region); + RegionEndpoint region = RegionEndpoint.GetBySystemName(options.Region); AmazonS3Config config = new() { RegionEndpoint = region, UseAccelerateEndpoint = options.UseAccelerateEndpoint }; SetTimeout(config, options.Timeout); return new AmazonS3Client(options.AccessKey, options.AccessSecret, config); } else if (!string.IsNullOrWhiteSpace(options.Region)) { - var region = RegionEndpoint.GetBySystemName(options.Region); + RegionEndpoint region = RegionEndpoint.GetBySystemName(options.Region); AmazonS3Config config = new() { RegionEndpoint = region, UseAccelerateEndpoint = options.UseAccelerateEndpoint }; SetTimeout(config, options.Timeout); return new AmazonS3Client(config); diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageImageProviderFactory.cs b/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageImageProviderFactory.cs index 5af0560e..bbfb8a5d 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageImageProviderFactory.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageImageProviderFactory.cs @@ -44,7 +44,7 @@ private static async Task InitializeAWSStorageAsync(IServiceProvider services, A { try { - var putBucketRequest = new PutBucketRequest + PutBucketRequest putBucketRequest = new() { BucketName = bucketOptions.BucketName, BucketRegion = bucketOptions.Region, @@ -57,7 +57,7 @@ private static async Task InitializeAWSStorageAsync(IServiceProvider services, A { // CI tests are run in parallel and can sometimes return a // false negative for the existance of a bucket. - if (string.Equals(e.ErrorCode, "BucketAlreadyExists")) + if (string.Equals(e.ErrorCode, "BucketAlreadyExists", StringComparison.Ordinal)) { return; } @@ -84,14 +84,14 @@ private static async Task InitializeAWSStorageAsync(IServiceProvider services, A using Stream stream = file.CreateReadStream(); // Set the max-age property so we get coverage for testing in our AWS provider. - var cacheControl = new CacheControlHeaderValue + CacheControlHeaderValue cacheControl = new() { Public = true, MaxAge = TimeSpan.FromDays(7), MustRevalidate = true }; - var putRequest = new PutObjectRequest() + PutObjectRequest putRequest = new() { BucketName = bucketOptions.BucketName, Key = TestConstants.ImagePath, diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs b/tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs index 09a187cd..12ed053d 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs @@ -21,9 +21,7 @@ public abstract class TestServerFixture : IDisposable private TestServer server; private bool isDisposed; -#pragma warning disable RCS1160 // Abstract type should not have public constructors. - public TestServerFixture() -#pragma warning restore RCS1160 // Abstract type should not have public constructors. + protected TestServerFixture() { IConfigurationRoot configuration = new ConfigurationBuilder() .AddInMemoryCollection(new[] From 548ec456f7c1a0fbf53aabd4b8f336cd0cbc703e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 Feb 2023 21:29:29 +1000 Subject: [PATCH 06/34] Skip macOS to test other environments --- .github/workflows/build-and-test.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 06f64fdb..1a13a7f9 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -20,11 +20,11 @@ jobs: sdk-preview: true runtime: -x64 codecov: false - - os: macos-latest - framework: net7.0 - sdk-preview: true - runtime: -x64 - codecov: false + #- os: macos-latest + # framework: net7.0 + # sdk-preview: true + # runtime: -x64 + # codecov: false - os: windows-latest framework: net7.0 sdk-preview: true @@ -34,10 +34,10 @@ jobs: framework: net6.0 runtime: -x64 codecov: false - - os: macos-latest - framework: net6.0 - runtime: -x64 - codecov: false + #- os: macos-latest + # framework: net6.0 + # runtime: -x64 + # codecov: false - os: windows-latest framework: net6.0 runtime: -x64 From e5349d8065534e2097be2e7dd738eaf2a0a7742a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 Feb 2023 21:38:39 +1000 Subject: [PATCH 07/34] Revert action changes --- .github/workflows/build-and-test.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 1a13a7f9..97aafb97 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -20,11 +20,11 @@ jobs: sdk-preview: true runtime: -x64 codecov: false - #- os: macos-latest - # framework: net7.0 - # sdk-preview: true - # runtime: -x64 - # codecov: false + - os: macos-latest + framework: net7.0 + sdk-preview: true + runtime: -x64 + codecov: false - os: windows-latest framework: net7.0 sdk-preview: true @@ -34,10 +34,10 @@ jobs: framework: net6.0 runtime: -x64 codecov: false - #- os: macos-latest - # framework: net6.0 - # runtime: -x64 - # codecov: false + - os: macos-latest + framework: net6.0 + runtime: -x64 + codecov: false - os: windows-latest framework: net6.0 runtime: -x64 @@ -101,15 +101,15 @@ jobs: if: matrix.options.os != 'windows-latest' shell: bash run: | - sudo npm install s3rver - sudo npx s3rver -d . & + sudo npm install -g s3rver + sudo s3rver -d . & - name: S3rver Setup Windows if: matrix.options.os == 'windows-latest' shell: bash run: | - npm install s3rver - npx s3rver -d . & + npm install -g s3rver + s3rver -d . & - name: DotNet Setup uses: actions/setup-dotnet@v3 From 5a1ac6af082282b4df50c84885186efeba942210 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 Feb 2023 21:41:14 +1000 Subject: [PATCH 08/34] Skip to see if Azurite works --- tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs b/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs index 6d8b5e83..9bac966f 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs @@ -36,7 +36,7 @@ protected ServerTestBase(TFixture fixture, ITestOutputHelper outputHelper, strin public string ImageSource { get; } - [Fact] + [Fact(Skip = "Investigating connection refused errors")] public async Task CanProcessAndResolveImageAsync() { string url = this.ImageSource; @@ -115,7 +115,7 @@ public async Task CanProcessAndResolveImageAsync() response.Dispose(); } - [Fact] + [Fact(Skip = "Investigating connection refused errors")] public async Task CanProcessMultipleIdenticalQueriesAsync() { string url = this.ImageSource; From 1a71bcf3dc50ab2a0414470d142fb14bda650c45 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 Feb 2023 21:47:09 +1000 Subject: [PATCH 09/34] Skip config for testing --- .../AWSS3StorageCacheTestServerFixture.cs | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheTestServerFixture.cs b/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheTestServerFixture.cs index cb016f22..3d40c28f 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheTestServerFixture.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheTestServerFixture.cs @@ -12,29 +12,33 @@ namespace SixLabors.ImageSharp.Web.Tests.TestUtilities; public class AWSS3StorageCacheTestServerFixture : TestServerFixture { protected override void ConfigureCustomServices(IServiceCollection services, IImageSharpBuilder builder) - => builder - .Configure(o => - o.S3Buckets.Add( - new AWSS3BucketClientOptions + { + return; + + builder + .Configure(o => + o.S3Buckets.Add( + new AWSS3BucketClientOptions + { + Endpoint = TestConstants.AWSEndpoint, + BucketName = TestConstants.AWSBucketName, + AccessKey = TestConstants.AWSAccessKey, + AccessSecret = TestConstants.AWSAccessSecret, + Region = TestConstants.AWSRegion, + Timeout = TestConstants.AWSTimeout, + })) + .AddProvider(AWSS3StorageImageProviderFactory.Create) + .Configure(o => { - Endpoint = TestConstants.AWSEndpoint, - BucketName = TestConstants.AWSBucketName, - AccessKey = TestConstants.AWSAccessKey, - AccessSecret = TestConstants.AWSAccessSecret, - Region = TestConstants.AWSRegion, - Timeout = TestConstants.AWSTimeout, - })) - .AddProvider(AWSS3StorageImageProviderFactory.Create) - .Configure(o => - { - o.Endpoint = TestConstants.AWSEndpoint; - o.BucketName = TestConstants.AWSCacheBucketName; - o.AccessKey = TestConstants.AWSAccessKey; - o.AccessSecret = TestConstants.AWSAccessSecret; - o.Region = TestConstants.AWSRegion; - o.Timeout = TestConstants.AWSTimeout; + o.Endpoint = TestConstants.AWSEndpoint; + o.BucketName = TestConstants.AWSCacheBucketName; + o.AccessKey = TestConstants.AWSAccessKey; + o.AccessSecret = TestConstants.AWSAccessSecret; + o.Region = TestConstants.AWSRegion; + o.Timeout = TestConstants.AWSTimeout; - AWSS3StorageCache.CreateIfNotExists(o, S3CannedACL.Private); - }) - .SetCache(); + AWSS3StorageCache.CreateIfNotExists(o, S3CannedACL.Private); + }) + .SetCache(); + } } From 0c176ac0b6a2f675e802c38752b93d241ad8aedb Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 Feb 2023 22:00:56 +1000 Subject: [PATCH 10/34] Try using a different port --- .github/workflows/build-and-test.yml | 4 +- .../AWSS3StorageCacheTestServerFixture.cs | 50 +++++++++---------- .../TestUtilities/ServerTestBase.cs | 4 +- .../TestUtilities/TestConstants.cs | 2 +- 4 files changed, 28 insertions(+), 32 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 97aafb97..952d2d1d 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -102,14 +102,14 @@ jobs: shell: bash run: | sudo npm install -g s3rver - sudo s3rver -d . & + sudo s3rver -p 10003 -d . & - name: S3rver Setup Windows if: matrix.options.os == 'windows-latest' shell: bash run: | npm install -g s3rver - s3rver -d . & + s3rver -p 10003 -d . & - name: DotNet Setup uses: actions/setup-dotnet@v3 diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheTestServerFixture.cs b/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheTestServerFixture.cs index 3d40c28f..cb016f22 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheTestServerFixture.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheTestServerFixture.cs @@ -12,33 +12,29 @@ namespace SixLabors.ImageSharp.Web.Tests.TestUtilities; public class AWSS3StorageCacheTestServerFixture : TestServerFixture { protected override void ConfigureCustomServices(IServiceCollection services, IImageSharpBuilder builder) - { - return; - - builder - .Configure(o => - o.S3Buckets.Add( - new AWSS3BucketClientOptions - { - Endpoint = TestConstants.AWSEndpoint, - BucketName = TestConstants.AWSBucketName, - AccessKey = TestConstants.AWSAccessKey, - AccessSecret = TestConstants.AWSAccessSecret, - Region = TestConstants.AWSRegion, - Timeout = TestConstants.AWSTimeout, - })) - .AddProvider(AWSS3StorageImageProviderFactory.Create) - .Configure(o => + => builder + .Configure(o => + o.S3Buckets.Add( + new AWSS3BucketClientOptions { - o.Endpoint = TestConstants.AWSEndpoint; - o.BucketName = TestConstants.AWSCacheBucketName; - o.AccessKey = TestConstants.AWSAccessKey; - o.AccessSecret = TestConstants.AWSAccessSecret; - o.Region = TestConstants.AWSRegion; - o.Timeout = TestConstants.AWSTimeout; + Endpoint = TestConstants.AWSEndpoint, + BucketName = TestConstants.AWSBucketName, + AccessKey = TestConstants.AWSAccessKey, + AccessSecret = TestConstants.AWSAccessSecret, + Region = TestConstants.AWSRegion, + Timeout = TestConstants.AWSTimeout, + })) + .AddProvider(AWSS3StorageImageProviderFactory.Create) + .Configure(o => + { + o.Endpoint = TestConstants.AWSEndpoint; + o.BucketName = TestConstants.AWSCacheBucketName; + o.AccessKey = TestConstants.AWSAccessKey; + o.AccessSecret = TestConstants.AWSAccessSecret; + o.Region = TestConstants.AWSRegion; + o.Timeout = TestConstants.AWSTimeout; - AWSS3StorageCache.CreateIfNotExists(o, S3CannedACL.Private); - }) - .SetCache(); - } + AWSS3StorageCache.CreateIfNotExists(o, S3CannedACL.Private); + }) + .SetCache(); } diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs b/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs index 9bac966f..6d8b5e83 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs @@ -36,7 +36,7 @@ protected ServerTestBase(TFixture fixture, ITestOutputHelper outputHelper, strin public string ImageSource { get; } - [Fact(Skip = "Investigating connection refused errors")] + [Fact] public async Task CanProcessAndResolveImageAsync() { string url = this.ImageSource; @@ -115,7 +115,7 @@ public async Task CanProcessAndResolveImageAsync() response.Dispose(); } - [Fact(Skip = "Investigating connection refused errors")] + [Fact] public async Task CanProcessMultipleIdenticalQueriesAsync() { string url = this.ImageSource; diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs b/tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs index bc1ba269..7e1aee78 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs @@ -8,7 +8,7 @@ public static class TestConstants public const string AzureConnectionString = "UseDevelopmentStorage=true"; public const string AzureContainerName = "azure"; public const string AzureCacheContainerName = "is-cache"; - public const string AWSEndpoint = "http://127.0.0.1:4568/"; + public const string AWSEndpoint = "http://127.0.0.1:10003/"; public const string AWSRegion = "eu-west-2"; public const string AWSBucketName = "aws"; public const string AWSCacheBucketName = "aws-cache"; From 85d84f7ad58970008f186d50f6f2a460bcd239b3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 Feb 2023 22:09:11 +1000 Subject: [PATCH 11/34] Try specifying localhost --- tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs b/tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs index 7e1aee78..c4529eff 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs @@ -8,7 +8,7 @@ public static class TestConstants public const string AzureConnectionString = "UseDevelopmentStorage=true"; public const string AzureContainerName = "azure"; public const string AzureCacheContainerName = "is-cache"; - public const string AWSEndpoint = "http://127.0.0.1:10003/"; + public const string AWSEndpoint = "http://localhost:10003/"; public const string AWSRegion = "eu-west-2"; public const string AWSBucketName = "aws"; public const string AWSCacheBucketName = "aws-cache"; From b86a433422478ed99ce9155816084b44be29c7b9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 Feb 2023 22:12:57 +1000 Subject: [PATCH 12/34] Use default configuration --- .github/workflows/build-and-test.yml | 4 ++-- tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 952d2d1d..97aafb97 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -102,14 +102,14 @@ jobs: shell: bash run: | sudo npm install -g s3rver - sudo s3rver -p 10003 -d . & + sudo s3rver -d . & - name: S3rver Setup Windows if: matrix.options.os == 'windows-latest' shell: bash run: | npm install -g s3rver - s3rver -p 10003 -d . & + s3rver -d . & - name: DotNet Setup uses: actions/setup-dotnet@v3 diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs b/tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs index c4529eff..26c64845 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/TestConstants.cs @@ -8,7 +8,7 @@ public static class TestConstants public const string AzureConnectionString = "UseDevelopmentStorage=true"; public const string AzureContainerName = "azure"; public const string AzureCacheContainerName = "is-cache"; - public const string AWSEndpoint = "http://localhost:10003/"; + public const string AWSEndpoint = "http://localhost:4568/"; public const string AWSRegion = "eu-west-2"; public const string AWSBucketName = "aws"; public const string AWSCacheBucketName = "aws-cache"; From 97bea0725579e30fcc1923a35a109c617172b498 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 Feb 2023 22:28:22 +1000 Subject: [PATCH 13/34] Test OnBeforeLoadAsync --- .../TestUtilities/TestServerFixture.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs b/tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs index 12ed053d..a9f68eed 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/TestServerFixture.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Web.DependencyInjection; using SixLabors.ImageSharp.Web.Middleware; @@ -67,6 +68,16 @@ protected void ConfigureServices(IServiceCollection services) return onParseCommandsAsync.Invoke(context); }; + Func onBeforeLoadAsync = options.OnBeforeLoadAsync; + + options.OnBeforeLoadAsync = (context, decoderOptions) => + { + Assert.NotNull(context); + Assert.NotNull(decoderOptions); + + return onBeforeLoadAsync.Invoke(context, decoderOptions); + }; + Func onProcessedAsync = options.OnProcessedAsync; options.OnProcessedAsync = context => From 2239a9a471b06b977d3f0e1b7879cbfbe844003a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 27 Feb 2023 23:47:23 +1000 Subject: [PATCH 14/34] Update to latest build --- shared-infrastructure | 2 +- src/ImageSharp.Web/ImageSharp.Web.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shared-infrastructure b/shared-infrastructure index 777842e3..9a6cf00d 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 777842e38622ab374e03f4076cf2544ed54a062b +Subproject commit 9a6cf00d9a3d482bb08211dd8309f4724a2735cb diff --git a/src/ImageSharp.Web/ImageSharp.Web.csproj b/src/ImageSharp.Web/ImageSharp.Web.csproj index 87b6d8cb..a4f69af8 100644 --- a/src/ImageSharp.Web/ImageSharp.Web.csproj +++ b/src/ImageSharp.Web/ImageSharp.Web.csproj @@ -45,7 +45,7 @@ - + From 43aa837af4d86b5035d17336a997898e4312fc4b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 27 Feb 2023 23:54:07 +1000 Subject: [PATCH 15/34] Update QualityWebProcessor.cs --- src/ImageSharp.Web/Processors/QualityWebProcessor.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp.Web/Processors/QualityWebProcessor.cs b/src/ImageSharp.Web/Processors/QualityWebProcessor.cs index 81a3b8fb..84af1804 100644 --- a/src/ImageSharp.Web/Processors/QualityWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/QualityWebProcessor.cs @@ -53,7 +53,13 @@ public FormattedImage Process( if (quality != reference.Quality) { - image.Encoder = new JpegEncoder() { Quality = quality, ColorType = reference.ColorType }; + image.Encoder = new JpegEncoder() + { + Quality = quality, + Interleaved = reference.Interleaved, + ColorType = reference.ColorType, + SkipMetadata = reference.SkipMetadata + }; } } else if (image.Format is WebpFormat) @@ -76,6 +82,7 @@ public FormattedImage Process( TransparentColorMode = reference.TransparentColorMode, NearLossless = reference.NearLossless, NearLosslessQuality = reference.NearLosslessQuality, + SkipMetadata = reference.SkipMetadata }; } } From d1ffdf8706b0ee64d34bb684b5ffc22ba78fe4ad Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 1 Mar 2023 20:22:45 +0100 Subject: [PATCH 16/34] Add Parsing of TargetRectangle to ResizeWebProcessor --- .../Processors/ResizeWebProcessor.cs | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs b/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs index d4c1b2de..f85447a7 100644 --- a/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs @@ -1,5 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. + #nullable disable using System.Globalization; @@ -61,6 +62,26 @@ public class ResizeWebProcessor : IImageWebProcessor /// public const string Compand = "compand"; + /// + /// The command constant for the resize TargetRectangle X + /// + public const string TargetRectangleX = "targetrectanglex"; + + /// + /// The command constant for the resize TargetRectangle X + /// + public const string TargetRectangleY = "targetrectangley"; + + /// + /// The command constant for the resize TargetRectangle X + /// + public const string TargetRectangleWidth = "targetrectanglewidth"; + + /// + /// The command constant for the resize TargetRectangle X + /// + public const string TargetRectangleHeight = "targetrectangleheight"; + private static readonly IEnumerable ResizeCommands = new[] { @@ -126,6 +147,8 @@ internal static ResizeOptions GetResizeOptions( return null; } + Rectangle? targetRectangle = ParseRectangle(commands, parser, culture); + return new() { Size = size, @@ -134,7 +157,8 @@ internal static ResizeOptions GetResizeOptions( Mode = GetMode(commands, parser, culture), Compand = GetCompandMode(commands, parser, culture), Sampler = GetSampler(commands), - PadColor = parser.ParseValue(commands.GetValueOrDefault(Color), culture) + PadColor = parser.ParseValue(commands.GetValueOrDefault(Color), culture), + TargetRectangle = targetRectangle }; } @@ -158,6 +182,31 @@ private static Size ParseSize( return ExifOrientationUtilities.Transform(new Size(width, height), orientation); } + private static Rectangle? ParseRectangle( + CommandCollection commands, + CommandParser parser, + CultureInfo culture) + { + if (!commands.Contains(TargetRectangleWidth) || !commands.Contains(TargetRectangleHeight)) + { + return null; + } + + // The command parser will reject negative numbers as it clamps values to ranges. + int width = (int)parser.ParseValue(commands.GetValueOrDefault(TargetRectangleWidth), culture); + int height = (int)parser.ParseValue(commands.GetValueOrDefault(TargetRectangleHeight), culture); + + if (!commands.Contains(TargetRectangleX) || !commands.Contains(TargetRectangleY)) + { + return new Rectangle(0, 0, width, height); + } + + int x = (int)parser.ParseValue(commands.GetValueOrDefault(TargetRectangleX), culture); + int y = (int)parser.ParseValue(commands.GetValueOrDefault(TargetRectangleY), culture); + + return new Rectangle(x, y, width, height); + } + private static PointF? GetCenter( ushort orientation, CommandCollection commands, From 5f0f77b8bd14efd1a0185f598970c466abb957d8 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 1 Mar 2023 20:33:24 +0100 Subject: [PATCH 17/34] Remove nullabel disable First batch of removing nullable disable --- src/ImageSharp.Web/AsyncHelper.cs | 1 - src/ImageSharp.Web/CaseHandlingUriBuilder.cs | 1 - src/ImageSharp.Web/CommandHandling.cs | 1 - .../ExifOrientationUtilities.cs | 1 - src/ImageSharp.Web/FormatUtilities.cs | 4 ++-- src/ImageSharp.Web/HMACUtilities.cs | 3 +-- src/ImageSharp.Web/ImageCacheMetadata.cs | 16 +++++++------- src/ImageSharp.Web/ImageMetadata.cs | 3 +-- ...ImageSharpRequestAuthorizationUtilities.cs | 21 +++++++++---------- src/ImageSharp.Web/PathUtilities.cs | 1 - 10 files changed, 22 insertions(+), 30 deletions(-) diff --git a/src/ImageSharp.Web/AsyncHelper.cs b/src/ImageSharp.Web/AsyncHelper.cs index 8bc52d93..3ba68fcb 100644 --- a/src/ImageSharp.Web/AsyncHelper.cs +++ b/src/ImageSharp.Web/AsyncHelper.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; diff --git a/src/ImageSharp.Web/CaseHandlingUriBuilder.cs b/src/ImageSharp.Web/CaseHandlingUriBuilder.cs index f257fa08..e3418820 100644 --- a/src/ImageSharp.Web/CaseHandlingUriBuilder.cs +++ b/src/ImageSharp.Web/CaseHandlingUriBuilder.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Runtime.CompilerServices; diff --git a/src/ImageSharp.Web/CommandHandling.cs b/src/ImageSharp.Web/CommandHandling.cs index 02f2e19b..2e436ea6 100644 --- a/src/ImageSharp.Web/CommandHandling.cs +++ b/src/ImageSharp.Web/CommandHandling.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using SixLabors.ImageSharp.Web.Commands; diff --git a/src/ImageSharp.Web/ExifOrientationUtilities.cs b/src/ImageSharp.Web/ExifOrientationUtilities.cs index 2e94f50a..dab77e8c 100644 --- a/src/ImageSharp.Web/ExifOrientationUtilities.cs +++ b/src/ImageSharp.Web/ExifOrientationUtilities.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; diff --git a/src/ImageSharp.Web/FormatUtilities.cs b/src/ImageSharp.Web/FormatUtilities.cs index 2d7da4ea..4e9cba52 100644 --- a/src/ImageSharp.Web/FormatUtilities.cs +++ b/src/ImageSharp.Web/FormatUtilities.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Options; @@ -51,7 +51,7 @@ public FormatUtilities(IOptions options) /// if the uri contains an extension; otherwise, . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetExtensionFromUri(string uri, out string extension) + public bool TryGetExtensionFromUri(string uri, [NotNullWhen(true)] out string? extension) { extension = null; int query = uri.IndexOf('?'); diff --git a/src/ImageSharp.Web/HMACUtilities.cs b/src/ImageSharp.Web/HMACUtilities.cs index eb9d497a..f90d8c52 100644 --- a/src/ImageSharp.Web/HMACUtilities.cs +++ b/src/ImageSharp.Web/HMACUtilities.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Runtime.CompilerServices; @@ -70,7 +69,7 @@ public static unsafe string ComputeHMACSHA512(string value, byte[] secret) private static unsafe string CreateHMAC(string value, HMAC hmac) { int byteCount = Encoding.ASCII.GetByteCount(value); - byte[] buffer = null; + byte[]? buffer = null; try { diff --git a/src/ImageSharp.Web/ImageCacheMetadata.cs b/src/ImageSharp.Web/ImageCacheMetadata.cs index 0e1ce20d..d871fd8d 100644 --- a/src/ImageSharp.Web/ImageCacheMetadata.cs +++ b/src/ImageSharp.Web/ImageCacheMetadata.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Runtime.CompilerServices; @@ -98,23 +97,24 @@ public ImageCacheMetadata( public static ImageCacheMetadata FromDictionary(IDictionary dictionary) { // DateTime.TryParse(null) == DateTime.MinValue so no need for conditional; - dictionary.TryGetValue(SourceLastModifiedKey, out string sourceLastWriteTimeUtcString); + dictionary.TryGetValue(SourceLastModifiedKey, out string? sourceLastWriteTimeUtcString); DateTime.TryParse(sourceLastWriteTimeUtcString, null, DateTimeStyles.RoundtripKind, out DateTime sourceLastWriteTimeUtc); - dictionary.TryGetValue(CacheLastModifiedKey, out string cacheLastWriteTimeUtcString); + dictionary.TryGetValue(CacheLastModifiedKey, out string? cacheLastWriteTimeUtcString); DateTime.TryParse(cacheLastWriteTimeUtcString, null, DateTimeStyles.RoundtripKind, out DateTime cacheLastWriteTimeUtc); - dictionary.TryGetValue(ContentTypeKey, out string contentType); + dictionary.TryGetValue(ContentTypeKey, out string? contentType); + Guard.NotNull(contentType); // int.TryParse(null) == 0 and we want to return TimeSpan.MinValue not TimeSpan.Zero TimeSpan cacheControlMaxAge = TimeSpan.MinValue; - if (dictionary.TryGetValue(CacheControlKey, out string cacheControlMaxAgeString)) + if (dictionary.TryGetValue(CacheControlKey, out string? cacheControlMaxAgeString)) { _ = int.TryParse(cacheControlMaxAgeString, out int maxAge); cacheControlMaxAge = TimeSpan.FromSeconds(maxAge); } - dictionary.TryGetValue(ContentLengthKey, out string contentLengthString); + dictionary.TryGetValue(ContentLengthKey, out string? contentLengthString); _ = long.TryParse(contentLengthString, out long contentLength); return new ImageCacheMetadata( @@ -135,7 +135,7 @@ public static async Task ReadAsync(Stream stream) Dictionary dictionary = new(); using (StreamReader reader = new(stream, Encoding.UTF8)) { - string line; + string? line; while ((line = await reader.ReadLineAsync()) != null) { int idx = line.IndexOf(':'); @@ -160,7 +160,7 @@ public bool Equals(ImageCacheMetadata other) && this.ContentLength == other.ContentLength; /// - public override bool Equals(object obj) + public override bool Equals(object? obj) => obj is ImageCacheMetadata data && this.Equals(data); /// diff --git a/src/ImageSharp.Web/ImageMetadata.cs b/src/ImageSharp.Web/ImageMetadata.cs index 9ff37df7..ba2d5462 100644 --- a/src/ImageSharp.Web/ImageMetadata.cs +++ b/src/ImageSharp.Web/ImageMetadata.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Runtime.CompilerServices; @@ -90,7 +89,7 @@ public bool Equals(ImageMetadata other) && this.ContentLength == other.ContentLength; /// - public override bool Equals(object obj) + public override bool Equals(object? obj) => obj is ImageMetadata data && this.Equals(data); /// diff --git a/src/ImageSharp.Web/ImageSharpRequestAuthorizationUtilities.cs b/src/ImageSharp.Web/ImageSharpRequestAuthorizationUtilities.cs index 18a5c502..7c1aede5 100644 --- a/src/ImageSharp.Web/ImageSharpRequestAuthorizationUtilities.cs +++ b/src/ImageSharp.Web/ImageSharpRequestAuthorizationUtilities.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using Microsoft.AspNetCore.Http; @@ -96,7 +95,7 @@ public void StripUnknownCommands(CommandCollection commands) /// The uri to compute the code from. /// The command collection handling. /// The computed HMAC. - public string ComputeHMAC(string uri, CommandHandling handling) + public string? ComputeHMAC(string uri, CommandHandling handling) => this.ComputeHMAC(new Uri(uri, UriKind.RelativeOrAbsolute), handling); /// @@ -105,7 +104,7 @@ public string ComputeHMAC(string uri, CommandHandling handling) /// The uri to compute the code from. /// The command collection handling. /// The computed HMAC. - public Task ComputeHMACAsync(string uri, CommandHandling handling) + public Task ComputeHMACAsync(string uri, CommandHandling handling) => this.ComputeHMACAsync(new Uri(uri, UriKind.RelativeOrAbsolute), handling); /// @@ -114,7 +113,7 @@ public Task ComputeHMACAsync(string uri, CommandHandling handling) /// The uri to compute the code from. /// The command collection handling. /// The computed HMAC. - public string ComputeHMAC(Uri uri, CommandHandling handling) + public string? ComputeHMAC(Uri uri, CommandHandling handling) { ToComponents( uri, @@ -131,7 +130,7 @@ public string ComputeHMAC(Uri uri, CommandHandling handling) /// The uri to compute the code from. /// The command collection handling. /// The computed HMAC. - public Task ComputeHMACAsync(Uri uri, CommandHandling handling) + public Task ComputeHMACAsync(Uri uri, CommandHandling handling) { ToComponents( uri, @@ -150,7 +149,7 @@ public Task ComputeHMACAsync(Uri uri, CommandHandling handling) /// The querystring. /// The command collection handling. /// The computed HMAC. - public string ComputeHMAC(HostString host, PathString path, QueryString queryString, CommandHandling handling) + public string? ComputeHMAC(HostString host, PathString path, QueryString queryString, CommandHandling handling) => this.ComputeHMAC(host, path, queryString, new(QueryHelpers.ParseQuery(queryString.Value)), handling); /// @@ -161,7 +160,7 @@ public string ComputeHMAC(HostString host, PathString path, QueryString queryStr /// The querystring. /// The command collection handling. /// The computed HMAC. - public Task ComputeHMACAsync(HostString host, PathString path, QueryString queryString, CommandHandling handling) + public Task ComputeHMACAsync(HostString host, PathString path, QueryString queryString, CommandHandling handling) => this.ComputeHMACAsync(host, path, queryString, new(QueryHelpers.ParseQuery(queryString.Value)), handling); /// @@ -173,7 +172,7 @@ public Task ComputeHMACAsync(HostString host, PathString path, QueryStri /// The query collection. /// The command collection handling. /// The computed HMAC. - public string ComputeHMAC(HostString host, PathString path, QueryString queryString, QueryCollection query, CommandHandling handling) + public string? ComputeHMAC(HostString host, PathString path, QueryString queryString, QueryCollection query, CommandHandling handling) => this.ComputeHMAC(this.ToHttpContext(host, path, queryString, query), handling); /// @@ -185,7 +184,7 @@ public string ComputeHMAC(HostString host, PathString path, QueryString queryStr /// The query collection. /// The command collection handling. /// The computed HMAC. - public Task ComputeHMACAsync(HostString host, PathString path, QueryString queryString, QueryCollection query, CommandHandling handling) + public Task ComputeHMACAsync(HostString host, PathString path, QueryString queryString, QueryCollection query, CommandHandling handling) => this.ComputeHMACAsync(this.ToHttpContext(host, path, queryString, query), handling); /// @@ -194,7 +193,7 @@ public Task ComputeHMACAsync(HostString host, PathString path, QueryStri /// The request HTTP context. /// The command collection handling. /// The computed HMAC. - public string ComputeHMAC(HttpContext context, CommandHandling handling) + public string? ComputeHMAC(HttpContext context, CommandHandling handling) => AsyncHelper.RunSync(() => this.ComputeHMACAsync(context, handling)); /// @@ -203,7 +202,7 @@ public string ComputeHMAC(HttpContext context, CommandHandling handling) /// The request HTTP context. /// The command collection handling. /// The computed HMAC. - public async Task ComputeHMACAsync(HttpContext context, CommandHandling handling) + public async Task ComputeHMACAsync(HttpContext context, CommandHandling handling) { byte[] secret = this.options.HMACSecretKey; if (secret is null || secret.Length == 0) diff --git a/src/ImageSharp.Web/PathUtilities.cs b/src/ImageSharp.Web/PathUtilities.cs index e3f6ef6b..719b2e27 100644 --- a/src/ImageSharp.Web/PathUtilities.cs +++ b/src/ImageSharp.Web/PathUtilities.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Web; From 558e582726acd225fea0962e4d6b1c3221e97710 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 1 Mar 2023 20:44:07 +0100 Subject: [PATCH 18/34] Remove nullable disable in Resolvers --- src/ImageSharp.Web/Resolvers/FileProviderImageResolver.cs | 1 - src/ImageSharp.Web/Resolvers/IImageCacheResolver.cs | 1 - src/ImageSharp.Web/Resolvers/IImageResolver.cs | 1 - src/ImageSharp.Web/Resolvers/PhysicalFileSystemCacheResolver.cs | 1 - 4 files changed, 4 deletions(-) diff --git a/src/ImageSharp.Web/Resolvers/FileProviderImageResolver.cs b/src/ImageSharp.Web/Resolvers/FileProviderImageResolver.cs index af0c6bb0..d4d6f0f1 100644 --- a/src/ImageSharp.Web/Resolvers/FileProviderImageResolver.cs +++ b/src/ImageSharp.Web/Resolvers/FileProviderImageResolver.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.Extensions.FileProviders; diff --git a/src/ImageSharp.Web/Resolvers/IImageCacheResolver.cs b/src/ImageSharp.Web/Resolvers/IImageCacheResolver.cs index 2d2b784a..b629bb86 100644 --- a/src/ImageSharp.Web/Resolvers/IImageCacheResolver.cs +++ b/src/ImageSharp.Web/Resolvers/IImageCacheResolver.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Web.Resolvers; diff --git a/src/ImageSharp.Web/Resolvers/IImageResolver.cs b/src/ImageSharp.Web/Resolvers/IImageResolver.cs index 935702b9..72d6dfb0 100644 --- a/src/ImageSharp.Web/Resolvers/IImageResolver.cs +++ b/src/ImageSharp.Web/Resolvers/IImageResolver.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Web.Resolvers; diff --git a/src/ImageSharp.Web/Resolvers/PhysicalFileSystemCacheResolver.cs b/src/ImageSharp.Web/Resolvers/PhysicalFileSystemCacheResolver.cs index 6494079b..ab6f4936 100644 --- a/src/ImageSharp.Web/Resolvers/PhysicalFileSystemCacheResolver.cs +++ b/src/ImageSharp.Web/Resolvers/PhysicalFileSystemCacheResolver.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Web.Caching; From d38cf39fa6e1685788e477ac23ebfc6b6c0188da Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 1 Mar 2023 20:44:20 +0100 Subject: [PATCH 19/34] Remove nullable disable in providers --- src/ImageSharp.Web/Providers/FileProviderImageProvider.cs | 7 +++---- src/ImageSharp.Web/Providers/IImageProvider.cs | 3 +-- src/ImageSharp.Web/Providers/PhysicalFileSystemProvider.cs | 1 - .../Providers/PhysicalFileSystemProviderOptions.cs | 3 +-- src/ImageSharp.Web/Providers/WebRootImageProvider.cs | 1 - 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp.Web/Providers/FileProviderImageProvider.cs b/src/ImageSharp.Web/Providers/FileProviderImageProvider.cs index c7dc3a5b..2e0cb6d4 100644 --- a/src/ImageSharp.Web/Providers/FileProviderImageProvider.cs +++ b/src/ImageSharp.Web/Providers/FileProviderImageProvider.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; @@ -51,14 +50,14 @@ public virtual bool IsValidRequest(HttpContext context) => this.formatUtilities.TryGetExtensionFromUri(context.Request.GetDisplayUrl(), out _); /// - public Task GetAsync(HttpContext context) + public Task GetAsync(HttpContext context) { IFileInfo fileInfo = this.fileProvider.GetFileInfo(context.Request.Path.Value); if (!fileInfo.Exists) { - return Task.FromResult(null); + return Task.FromResult(null); } - return Task.FromResult(new FileProviderImageResolver(fileInfo)); + return Task.FromResult(new FileProviderImageResolver(fileInfo)); } } diff --git a/src/ImageSharp.Web/Providers/IImageProvider.cs b/src/ImageSharp.Web/Providers/IImageProvider.cs index a25e7525..6de0c68d 100644 --- a/src/ImageSharp.Web/Providers/IImageProvider.cs +++ b/src/ImageSharp.Web/Providers/IImageProvider.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Http; using SixLabors.ImageSharp.Web.Resolvers; @@ -36,5 +35,5 @@ public interface IImageProvider /// /// The current HTTP request context. /// The . - Task GetAsync(HttpContext context); + Task GetAsync(HttpContext context); } diff --git a/src/ImageSharp.Web/Providers/PhysicalFileSystemProvider.cs b/src/ImageSharp.Web/Providers/PhysicalFileSystemProvider.cs index dddfe2f2..7634e20f 100644 --- a/src/ImageSharp.Web/Providers/PhysicalFileSystemProvider.cs +++ b/src/ImageSharp.Web/Providers/PhysicalFileSystemProvider.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.FileProviders; diff --git a/src/ImageSharp.Web/Providers/PhysicalFileSystemProviderOptions.cs b/src/ImageSharp.Web/Providers/PhysicalFileSystemProviderOptions.cs index f7b6f42e..57aaa99f 100644 --- a/src/ImageSharp.Web/Providers/PhysicalFileSystemProviderOptions.cs +++ b/src/ImageSharp.Web/Providers/PhysicalFileSystemProviderOptions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Web.Providers; @@ -21,7 +20,7 @@ public class PhysicalFileSystemProviderOptions /// application content files; commonly 'wwwroot'. /// /// - public string ProviderRootPath { get; set; } + public string? ProviderRootPath { get; set; } /// /// Gets or sets the processing behavior. Defaults to . diff --git a/src/ImageSharp.Web/Providers/WebRootImageProvider.cs b/src/ImageSharp.Web/Providers/WebRootImageProvider.cs index 5c928dc3..a469dd11 100644 --- a/src/ImageSharp.Web/Providers/WebRootImageProvider.cs +++ b/src/ImageSharp.Web/Providers/WebRootImageProvider.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Hosting; From 5942354d267d89c2823027667e691348a2f03a6a Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 1 Mar 2023 20:52:09 +0100 Subject: [PATCH 20/34] Remove nullable disable from Processors --- src/ImageSharp.Web/Processors/AutoOrientWebProcessor.cs | 1 - src/ImageSharp.Web/Processors/BackgroundColorWebProcessor.cs | 1 - src/ImageSharp.Web/Processors/FormatWebProcessor.cs | 3 +-- src/ImageSharp.Web/Processors/IImageWebProcessor.cs | 1 - src/ImageSharp.Web/Processors/QualityWebProcessor.cs | 1 - src/ImageSharp.Web/Processors/ResizeWebProcessor.cs | 5 ++--- src/ImageSharp.Web/Processors/WebProcessingExtensions.cs | 1 - 7 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp.Web/Processors/AutoOrientWebProcessor.cs b/src/ImageSharp.Web/Processors/AutoOrientWebProcessor.cs index 9c3c4867..fc081169 100644 --- a/src/ImageSharp.Web/Processors/AutoOrientWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/AutoOrientWebProcessor.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using Microsoft.Extensions.Logging; diff --git a/src/ImageSharp.Web/Processors/BackgroundColorWebProcessor.cs b/src/ImageSharp.Web/Processors/BackgroundColorWebProcessor.cs index 79668e09..549fa716 100644 --- a/src/ImageSharp.Web/Processors/BackgroundColorWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/BackgroundColorWebProcessor.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using Microsoft.Extensions.Logging; diff --git a/src/ImageSharp.Web/Processors/FormatWebProcessor.cs b/src/ImageSharp.Web/Processors/FormatWebProcessor.cs index 0117e9b6..b4e94e9c 100644 --- a/src/ImageSharp.Web/Processors/FormatWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/FormatWebProcessor.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using Microsoft.Extensions.Logging; @@ -53,7 +52,7 @@ public FormattedImage Process( string extension = commands.GetValueOrDefault(Format); if (!string.IsNullOrWhiteSpace(extension) - && this.options.Configuration.ImageFormatsManager.TryFindFormatByFileExtension(extension, out IImageFormat format)) + && this.options.Configuration.ImageFormatsManager.TryFindFormatByFileExtension(extension, out IImageFormat? format)) { image.Format = format; } diff --git a/src/ImageSharp.Web/Processors/IImageWebProcessor.cs b/src/ImageSharp.Web/Processors/IImageWebProcessor.cs index 6e1f8a85..df0cdb22 100644 --- a/src/ImageSharp.Web/Processors/IImageWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/IImageWebProcessor.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using Microsoft.Extensions.Logging; diff --git a/src/ImageSharp.Web/Processors/QualityWebProcessor.cs b/src/ImageSharp.Web/Processors/QualityWebProcessor.cs index 84af1804..37d8e9da 100644 --- a/src/ImageSharp.Web/Processors/QualityWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/QualityWebProcessor.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using Microsoft.Extensions.Logging; diff --git a/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs b/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs index d4c1b2de..dbad7950 100644 --- a/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Numerics; @@ -86,7 +85,7 @@ public FormattedImage Process( CommandParser parser, CultureInfo culture) { - ResizeOptions options = GetResizeOptions(image, commands, parser, culture); + ResizeOptions? options = GetResizeOptions(image, commands, parser, culture); if (options != null) { @@ -106,7 +105,7 @@ public FormattedImage Process( /// The to use as the current parsing culture. /// /// The . - internal static ResizeOptions GetResizeOptions( + internal static ResizeOptions? GetResizeOptions( FormattedImage image, CommandCollection commands, CommandParser parser, diff --git a/src/ImageSharp.Web/Processors/WebProcessingExtensions.cs b/src/ImageSharp.Web/Processors/WebProcessingExtensions.cs index 4c568b0a..ada16b3d 100644 --- a/src/ImageSharp.Web/Processors/WebProcessingExtensions.cs +++ b/src/ImageSharp.Web/Processors/WebProcessingExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using Microsoft.Extensions.Logging; From d80b750fda752f1dc5305596f93fd414d976695a Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 1 Mar 2023 21:02:44 +0100 Subject: [PATCH 21/34] Remove nullable disable from Middleware --- .../Middleware/ImageCommandContext.cs | 1 - src/ImageSharp.Web/Middleware/ImageContext.cs | 3 +-- .../Middleware/ImageProcessingContext.cs | 1 - .../Middleware/ImageSharpMiddleware.cs | 27 ++++++++++--------- .../Middleware/ImageSharpMiddlewareOptions.cs | 1 - .../Middleware/ImageWorkerResult.cs | 5 ++-- .../Middleware/LoggerExtensions.cs | 9 +++---- 7 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/ImageSharp.Web/Middleware/ImageCommandContext.cs b/src/ImageSharp.Web/Middleware/ImageCommandContext.cs index 0bb06af9..228347f0 100644 --- a/src/ImageSharp.Web/Middleware/ImageCommandContext.cs +++ b/src/ImageSharp.Web/Middleware/ImageCommandContext.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using Microsoft.AspNetCore.Http; diff --git a/src/ImageSharp.Web/Middleware/ImageContext.cs b/src/ImageSharp.Web/Middleware/ImageContext.cs index f34058b1..697dc5e7 100644 --- a/src/ImageSharp.Web/Middleware/ImageContext.cs +++ b/src/ImageSharp.Web/Middleware/ImageContext.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; @@ -23,7 +22,7 @@ internal struct ImageContext private DateTimeOffset fileLastModified; private long fileLength; - private EntityTagHeaderValue fileEtag; + private EntityTagHeaderValue? fileEtag; private PreconditionState ifMatchState; private PreconditionState ifNoneMatchState; diff --git a/src/ImageSharp.Web/Middleware/ImageProcessingContext.cs b/src/ImageSharp.Web/Middleware/ImageProcessingContext.cs index a03a5508..53c0c3d1 100644 --- a/src/ImageSharp.Web/Middleware/ImageProcessingContext.cs +++ b/src/ImageSharp.Web/Middleware/ImageProcessingContext.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Http; using SixLabors.ImageSharp.Web.Commands; diff --git a/src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs b/src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs index 02424756..9b9e9b2b 100644 --- a/src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs +++ b/src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Diagnostics; using System.Globalization; @@ -33,7 +32,7 @@ private static readonly ConcurrentTLruCache SourceMetadat /// /// Used to temporarily store cache resolver reads to reduce the overhead of cache lookups. /// - private static readonly ConcurrentTLruCache CacheResolverLru + private static readonly ConcurrentTLruCache CacheResolverLru = new(1024, TimeSpan.FromSeconds(30)); /// @@ -186,7 +185,7 @@ public ImageSharpMiddleware( private async Task Invoke(HttpContext httpContext, bool retry) { // Get the correct provider for the request - IImageProvider provider = null; + IImageProvider? provider = null; foreach (IImageProvider resolver in this.providers) { if (resolver.Match(httpContext)) @@ -208,7 +207,7 @@ private async Task Invoke(HttpContext httpContext, bool retry) // First check for a HMAC token and capture before the command is stripped out. byte[] secret = this.options.HMACSecretKey; bool checkHMAC = false; - string token = null; + string? token = null; if (secret?.Length > 0) { checkHMAC = true; @@ -219,7 +218,7 @@ private async Task Invoke(HttpContext httpContext, bool retry) ImageCommandContext imageCommandContext = new(httpContext, commands, this.commandParser, this.parserCulture); // At this point we know that this is an image request so should attempt to compute a validating HMAC. - string hmac = null; + string? hmac = null; if (checkHMAC && token != null) { // Generate and cache a HMAC to validate against based upon the current valid commands from the request. @@ -254,7 +253,7 @@ private async Task Invoke(HttpContext httpContext, bool retry) } } - IImageResolver sourceImageResolver = await provider.GetAsync(httpContext); + IImageResolver? sourceImageResolver = await provider.GetAsync(httpContext); if (sourceImageResolver is null) { @@ -312,7 +311,7 @@ private async Task ProcessRequestAsync( // Enter an asynchronous write worker which prevents multiple writes and delays any reads for the same request. // This reduces the overheads of unnecessary processing. - RecyclableMemoryStream outStream = null; + RecyclableMemoryStream? outStream = null; try { Task takeLockTask = this.asyncKeyLock.WriterLockAsync(key); @@ -360,7 +359,7 @@ private async Task ProcessRequestAsync( } else { - FormattedImage image = null; + FormattedImage? image = null; try { // Now we can finally process the image. @@ -442,7 +441,7 @@ private async Task ProcessRequestAsync( } } - private static ValueTask StreamDisposeAsync(Stream stream) + private static ValueTask StreamDisposeAsync(Stream? stream) { if (stream is null) { @@ -464,12 +463,12 @@ private async Task IsNewOrUpdatedAsync( // Check to see if the cache contains this image. // If not, we return early. No further checks necessary. - (IImageCacheResolver ImageCacheResolver, ImageCacheMetadata ImageCacheMetadata) cachedImage = await + (IImageCacheResolver? ImageCacheResolver, ImageCacheMetadata ImageCacheMetadata) cachedImage = await CacheResolverLru.GetOrAddAsync( key, async k => { - IImageCacheResolver resolver = await this.cache.GetAsync(k); + IImageCacheResolver? resolver = await this.cache.GetAsync(k); ImageCacheMetadata metadata = default; if (resolver != null) { @@ -507,8 +506,8 @@ private async Task SendResponseAsync( ImageContext imageContext, string key, ImageCacheMetadata metadata, - IImageCacheResolver cacheResolver, - Stream stream, + IImageCacheResolver? cacheResolver, + Stream? stream, bool retry) { imageContext.ComprehendRequestHeaders(metadata.CacheLastWriteTimeUtc, metadata.ContentLength); @@ -535,6 +534,8 @@ private async Task SendResponseAsync( { try { + Guard.NotNull(cacheResolver); + using Stream cacheStream = await cacheResolver.OpenReadAsync(); await imageContext.SendAsync(cacheStream, metadata); } diff --git a/src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs b/src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs index 6c7bfd6e..a1753f35 100644 --- a/src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs +++ b/src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using Microsoft.AspNetCore.Http; diff --git a/src/ImageSharp.Web/Middleware/ImageWorkerResult.cs b/src/ImageSharp.Web/Middleware/ImageWorkerResult.cs index f15a66a4..ee25c663 100644 --- a/src/ImageSharp.Web/Middleware/ImageWorkerResult.cs +++ b/src/ImageSharp.Web/Middleware/ImageWorkerResult.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using SixLabors.ImageSharp.Web.Resolvers; @@ -27,7 +26,7 @@ public ImageWorkerResult(ImageMetadata sourceImageMetadata, ImageCacheMetadata c this.Resolver = resolver; } - public ImageWorkerResult(ImageCacheMetadata cacheImageMetadata, IImageCacheResolver resolver) + public ImageWorkerResult(ImageCacheMetadata cacheImageMetadata, IImageCacheResolver? resolver) { this.IsNewOrUpdated = false; this.SourceImageMetadata = default; @@ -41,5 +40,5 @@ public ImageWorkerResult(ImageCacheMetadata cacheImageMetadata, IImageCacheResol public ImageCacheMetadata CacheImageMetadata { get; } - public IImageCacheResolver Resolver { get; } + public IImageCacheResolver? Resolver { get; } } diff --git a/src/ImageSharp.Web/Middleware/LoggerExtensions.cs b/src/ImageSharp.Web/Middleware/LoggerExtensions.cs index 84195f02..7f158e8a 100644 --- a/src/ImageSharp.Web/Middleware/LoggerExtensions.cs +++ b/src/ImageSharp.Web/Middleware/LoggerExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.Extensions.Logging; @@ -12,10 +11,10 @@ namespace SixLabors.ImageSharp.Web.Middleware; internal static class LoggerExtensions { private static readonly Action LogProcessingErrorAction; - private static readonly Action LogResolveFailedAction; - private static readonly Action LogServedAction; - private static readonly Action LogPathNotModifiedAction; - private static readonly Action LogPreconditionFailedAction; + private static readonly Action LogResolveFailedAction; + private static readonly Action LogServedAction; + private static readonly Action LogPathNotModifiedAction; + private static readonly Action LogPreconditionFailedAction; /// /// Initializes static members of the class. From 844a4dc59eb3691235e623ee9f65e31b55ffb642 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 1 Mar 2023 21:05:39 +0100 Subject: [PATCH 22/34] Remove nullable disable from DependencyInjection --- .../DependencyInjection/ApplicationBuilderExtensions.cs | 1 - .../DependencyInjection/IImageSharpBuilder.cs | 1 - .../DependencyInjection/ImageSharpBuilder.cs | 1 - .../DependencyInjection/ImageSharpBuilderExtensions.cs | 9 ++++----- .../DependencyInjection/ServiceCollectionExtensions.cs | 1 - 5 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp.Web/DependencyInjection/ApplicationBuilderExtensions.cs b/src/ImageSharp.Web/DependencyInjection/ApplicationBuilderExtensions.cs index e3bdaf9c..e2f896dc 100644 --- a/src/ImageSharp.Web/DependencyInjection/ApplicationBuilderExtensions.cs +++ b/src/ImageSharp.Web/DependencyInjection/ApplicationBuilderExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Builder; using SixLabors.ImageSharp.Web.Middleware; diff --git a/src/ImageSharp.Web/DependencyInjection/IImageSharpBuilder.cs b/src/ImageSharp.Web/DependencyInjection/IImageSharpBuilder.cs index 34a2701d..f1935af9 100644 --- a/src/ImageSharp.Web/DependencyInjection/IImageSharpBuilder.cs +++ b/src/ImageSharp.Web/DependencyInjection/IImageSharpBuilder.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.Extensions.DependencyInjection; diff --git a/src/ImageSharp.Web/DependencyInjection/ImageSharpBuilder.cs b/src/ImageSharp.Web/DependencyInjection/ImageSharpBuilder.cs index ac44dfe8..58da1137 100644 --- a/src/ImageSharp.Web/DependencyInjection/ImageSharpBuilder.cs +++ b/src/ImageSharp.Web/DependencyInjection/ImageSharpBuilder.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.Extensions.DependencyInjection; diff --git a/src/ImageSharp.Web/DependencyInjection/ImageSharpBuilderExtensions.cs b/src/ImageSharp.Web/DependencyInjection/ImageSharpBuilderExtensions.cs index d696f015..8c4a17bb 100644 --- a/src/ImageSharp.Web/DependencyInjection/ImageSharpBuilderExtensions.cs +++ b/src/ImageSharp.Web/DependencyInjection/ImageSharpBuilderExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -205,7 +204,7 @@ public static IImageSharpBuilder InsertProvider(this IImageSharpBuild public static IImageSharpBuilder RemoveProvider(this IImageSharpBuilder builder) where TProvider : class, IImageProvider { - ServiceDescriptor descriptor = builder.Services.FirstOrDefault(x => x.ServiceType == typeof(IImageProvider) && x.GetImplementationType() == typeof(TProvider)); + ServiceDescriptor? descriptor = builder.Services.FirstOrDefault(x => x.ServiceType == typeof(IImageProvider) && x.GetImplementationType() == typeof(TProvider)); if (descriptor != null) { builder.Services.Remove(descriptor); @@ -264,7 +263,7 @@ public static IImageSharpBuilder AddProcessor(this IImageSharpBuilde public static IImageSharpBuilder RemoveProcessor(this IImageSharpBuilder builder) where TProcessor : class, IImageWebProcessor { - ServiceDescriptor descriptor = builder.Services.FirstOrDefault(x => x.ServiceType == typeof(IImageWebProcessor) && x.GetImplementationType() == typeof(TProcessor)); + ServiceDescriptor? descriptor = builder.Services.FirstOrDefault(x => x.ServiceType == typeof(IImageWebProcessor) && x.GetImplementationType() == typeof(TProcessor)); if (descriptor != null) { builder.Services.Remove(descriptor); @@ -323,7 +322,7 @@ public static IImageSharpBuilder AddConverter(this IImageSharpBuilde public static IImageSharpBuilder RemoveConverter(this IImageSharpBuilder builder) where TConverter : class, ICommandConverter { - ServiceDescriptor descriptor = builder.Services.FirstOrDefault(x => x.ServiceType == typeof(ICommandConverter) && x.GetImplementationType() == typeof(TConverter)); + ServiceDescriptor? descriptor = builder.Services.FirstOrDefault(x => x.ServiceType == typeof(ICommandConverter) && x.GetImplementationType() == typeof(TConverter)); if (descriptor != null) { builder.Services.Remove(descriptor); @@ -374,7 +373,7 @@ public static IImageSharpBuilder Configure(this IImageSharpBuilder bui return builder; } - private static Type GetImplementationType(this ServiceDescriptor descriptor) + private static Type? GetImplementationType(this ServiceDescriptor descriptor) => descriptor.ImplementationType ?? descriptor.ImplementationInstance?.GetType() ?? descriptor.ImplementationFactory?.GetType().GenericTypeArguments[1]; diff --git a/src/ImageSharp.Web/DependencyInjection/ServiceCollectionExtensions.cs b/src/ImageSharp.Web/DependencyInjection/ServiceCollectionExtensions.cs index 31fcd6ac..92729e1f 100644 --- a/src/ImageSharp.Web/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/ImageSharp.Web/DependencyInjection/ServiceCollectionExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.Extensions.DependencyInjection; using SixLabors.ImageSharp.Web.Caching; From bd96955e1ecfd76b5fe5d7d2ddb38549115567cb Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 1 Mar 2023 21:10:12 +0100 Subject: [PATCH 23/34] Remove nullable disable from Commands --- src/ImageSharp.Web/Commands/CommandCollection.cs | 7 ++++--- src/ImageSharp.Web/Commands/CommandCollectionExtensions.cs | 1 - src/ImageSharp.Web/Commands/CommandParser.cs | 5 ++--- src/ImageSharp.Web/Commands/IRequestParser.cs | 1 - .../Commands/PresetOnlyQueryCollectionRequestParser.cs | 3 +-- .../PresetOnlyQueryCollectionRequestParserOptions.cs | 1 - .../Commands/QueryCollectionRequestParser.cs | 1 - src/ImageSharp.Web/Commands/TypeConstants.cs | 1 - 8 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp.Web/Commands/CommandCollection.cs b/src/ImageSharp.Web/Commands/CommandCollection.cs index 1718384e..b81ada58 100644 --- a/src/ImageSharp.Web/Commands/CommandCollection.cs +++ b/src/ImageSharp.Web/Commands/CommandCollection.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Web.Commands; @@ -54,7 +54,7 @@ public IEnumerable Keys { get { - if (!this.TryGetValue(key, out string value)) + if (!this.TryGetValue(key, out string? value)) { ThrowKeyNotFound(); } @@ -107,7 +107,7 @@ public IEnumerable Keys /// an element with the specified key; otherwise, . /// /// is null. - public bool TryGetValue(string key, out string value) + public bool TryGetValue(string key, [NotNullWhen(true)] out string? value) { if (this.TryGetValue(key, out KeyValuePair keyValue)) { @@ -175,5 +175,6 @@ public int IndexOf(string key) protected override string GetKeyForItem(KeyValuePair item) => item.Key; [MethodImpl(MethodImplOptions.NoInlining)] + [DoesNotReturn] private static void ThrowKeyNotFound() => throw new KeyNotFoundException(); } diff --git a/src/ImageSharp.Web/Commands/CommandCollectionExtensions.cs b/src/ImageSharp.Web/Commands/CommandCollectionExtensions.cs index 6d6752ca..66c5e1fe 100644 --- a/src/ImageSharp.Web/Commands/CommandCollectionExtensions.cs +++ b/src/ImageSharp.Web/Commands/CommandCollectionExtensions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Web.Commands; diff --git a/src/ImageSharp.Web/Commands/CommandParser.cs b/src/ImageSharp.Web/Commands/CommandParser.cs index e527510a..c7c39e1c 100644 --- a/src/ImageSharp.Web/Commands/CommandParser.cs +++ b/src/ImageSharp.Web/Commands/CommandParser.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Net; @@ -36,12 +35,12 @@ public CommandParser(IEnumerable converters) /// /// The converted instance or the default. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T ParseValue(string value, CultureInfo culture) + public T? ParseValue(string value, CultureInfo culture) { DebugGuard.NotNull(culture, nameof(culture)); Type type = typeof(T); - ICommandConverter converter = Array.Find(this.converters, x => x.Type.Equals(type)); + ICommandConverter? converter = Array.Find(this.converters, x => x.Type.Equals(type)); if (converter != null) { diff --git a/src/ImageSharp.Web/Commands/IRequestParser.cs b/src/ImageSharp.Web/Commands/IRequestParser.cs index 4d4d418a..73aede92 100644 --- a/src/ImageSharp.Web/Commands/IRequestParser.cs +++ b/src/ImageSharp.Web/Commands/IRequestParser.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Http; diff --git a/src/ImageSharp.Web/Commands/PresetOnlyQueryCollectionRequestParser.cs b/src/ImageSharp.Web/Commands/PresetOnlyQueryCollectionRequestParser.cs index f4215369..15f7a4e1 100644 --- a/src/ImageSharp.Web/Commands/PresetOnlyQueryCollectionRequestParser.cs +++ b/src/ImageSharp.Web/Commands/PresetOnlyQueryCollectionRequestParser.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.WebUtilities; @@ -42,7 +41,7 @@ public CommandCollection ParseRequestCommands(HttpContext context) StringValues query = queryCollection[QueryKey]; string requestedPreset = query[query.Count - 1]; - if (this.presets.TryGetValue(requestedPreset, out CommandCollection collection)) + if (this.presets.TryGetValue(requestedPreset, out CommandCollection? collection)) { return collection; } diff --git a/src/ImageSharp.Web/Commands/PresetOnlyQueryCollectionRequestParserOptions.cs b/src/ImageSharp.Web/Commands/PresetOnlyQueryCollectionRequestParserOptions.cs index f12934f3..7c40e8a4 100644 --- a/src/ImageSharp.Web/Commands/PresetOnlyQueryCollectionRequestParserOptions.cs +++ b/src/ImageSharp.Web/Commands/PresetOnlyQueryCollectionRequestParserOptions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Web.Commands; diff --git a/src/ImageSharp.Web/Commands/QueryCollectionRequestParser.cs b/src/ImageSharp.Web/Commands/QueryCollectionRequestParser.cs index 9edf4ea4..c4ac88c9 100644 --- a/src/ImageSharp.Web/Commands/QueryCollectionRequestParser.cs +++ b/src/ImageSharp.Web/Commands/QueryCollectionRequestParser.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; diff --git a/src/ImageSharp.Web/Commands/TypeConstants.cs b/src/ImageSharp.Web/Commands/TypeConstants.cs index e7a0d2b6..c6460de5 100644 --- a/src/ImageSharp.Web/Commands/TypeConstants.cs +++ b/src/ImageSharp.Web/Commands/TypeConstants.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Web.Commands; From 1422dd09684e0f69876edbbef239bd233aaa6fc6 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 1 Mar 2023 21:22:47 +0100 Subject: [PATCH 24/34] Remove nullable disable from converters --- src/ImageSharp.Web/Commands/CommandCollection.cs | 16 ++++++++-------- .../Commands/CommandCollectionExtensions.cs | 4 ++-- src/ImageSharp.Web/Commands/CommandParser.cs | 6 ++++-- .../Commands/Converters/ArrayConverter{T}.cs | 5 ++--- .../Commands/Converters/ColorConverter.cs | 9 ++++----- .../Commands/Converters/EnumConverter.cs | 5 ++--- .../Commands/Converters/ICommandConverter.cs | 3 +-- .../Converters/IntegralNumberConverter{T}.cs | 3 +-- .../Commands/Converters/ListConverter{T}.cs | 5 ++--- .../Converters/SimpleCommandConverter{T}.cs | 7 +++---- .../Processors/FormatWebProcessor.cs | 2 +- .../Processors/ResizeWebProcessor.cs | 9 +++++++-- 12 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/ImageSharp.Web/Commands/CommandCollection.cs b/src/ImageSharp.Web/Commands/CommandCollection.cs index b81ada58..16747d48 100644 --- a/src/ImageSharp.Web/Commands/CommandCollection.cs +++ b/src/ImageSharp.Web/Commands/CommandCollection.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Web.Commands; /// /// Represents an ordered collection of processing commands. /// -public sealed class CommandCollection : KeyedCollection> +public sealed class CommandCollection : KeyedCollection> { /// /// Initializes a new instance of the class. @@ -32,7 +32,7 @@ public IEnumerable Keys { get { - foreach (KeyValuePair item in this) + foreach (KeyValuePair item in this) { yield return this.GetKeyForItem(item); } @@ -64,7 +64,7 @@ public IEnumerable Keys set { - if (this.TryGetValue(key, out KeyValuePair item)) + if (this.TryGetValue(key, out KeyValuePair item)) { this.SetItem(this.IndexOf(item), new(key, value)); } @@ -109,10 +109,10 @@ public IEnumerable Keys /// is null. public bool TryGetValue(string key, [NotNullWhen(true)] out string? value) { - if (this.TryGetValue(key, out KeyValuePair keyValue)) + if (this.TryGetValue(key, out KeyValuePair keyValue)) { value = keyValue.Value; - return true; + return value is not null; } value = default; @@ -138,7 +138,7 @@ public int FindIndex(Predicate match) Guard.NotNull(match, nameof(match)); int index = 0; - foreach (KeyValuePair item in this) + foreach (KeyValuePair item in this) { if (match(item.Key)) { @@ -162,7 +162,7 @@ public int FindIndex(Predicate match) /// public int IndexOf(string key) { - if (this.TryGetValue(key, out KeyValuePair item)) + if (this.TryGetValue(key, out KeyValuePair item)) { return this.IndexOf(item); } @@ -172,7 +172,7 @@ public int IndexOf(string key) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override string GetKeyForItem(KeyValuePair item) => item.Key; + protected override string GetKeyForItem(KeyValuePair item) => item.Key; [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] diff --git a/src/ImageSharp.Web/Commands/CommandCollectionExtensions.cs b/src/ImageSharp.Web/Commands/CommandCollectionExtensions.cs index 66c5e1fe..126c990a 100644 --- a/src/ImageSharp.Web/Commands/CommandCollectionExtensions.cs +++ b/src/ImageSharp.Web/Commands/CommandCollectionExtensions.cs @@ -14,9 +14,9 @@ public static class CommandCollectionExtensions /// The collection instance. /// The key of the value to get. /// The value associated with the specified key or the default value. - public static string GetValueOrDefault(this CommandCollection collection, string key) + public static string? GetValueOrDefault(this CommandCollection collection, string key) { - collection.TryGetValue(key, out KeyValuePair result); + collection.TryGetValue(key, out KeyValuePair result); return result.Value; } } diff --git a/src/ImageSharp.Web/Commands/CommandParser.cs b/src/ImageSharp.Web/Commands/CommandParser.cs index c7c39e1c..19704159 100644 --- a/src/ImageSharp.Web/Commands/CommandParser.cs +++ b/src/ImageSharp.Web/Commands/CommandParser.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Net; using System.Runtime.CompilerServices; @@ -35,7 +36,7 @@ public CommandParser(IEnumerable converters) /// /// The converted instance or the default. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T? ParseValue(string value, CultureInfo culture) + public T? ParseValue(string? value, CultureInfo culture) { DebugGuard.NotNull(culture, nameof(culture)); @@ -58,7 +59,7 @@ public CommandParser(IEnumerable converters) converter = Array.Find(this.converters, x => x.Type.Equals(typeof(Enum))); if (converter != null) { - return (T)((ICommandConverter)converter).ConvertFrom( + return (T?)((ICommandConverter)converter).ConvertFrom( this, culture, WebUtility.UrlDecode(value), @@ -73,6 +74,7 @@ public CommandParser(IEnumerable converters) } [MethodImpl(MethodImplOptions.NoInlining)] + [DoesNotReturn] private static void ThrowNotSupported(Type type) => throw new NotSupportedException($"Cannot convert to {type.FullName}."); } diff --git a/src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs b/src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs index 0616e18e..bb7e70c5 100644 --- a/src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs +++ b/src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Runtime.CompilerServices; @@ -21,7 +20,7 @@ public sealed class ArrayConverter : ICommandConverter public T[] ConvertFrom( CommandParser parser, CultureInfo culture, - string value, + string? value, Type propertyType) { if (string.IsNullOrWhiteSpace(value)) @@ -32,7 +31,7 @@ public T[] ConvertFrom( var result = new List(); foreach (string pill in GetStringArray(value, culture)) { - T item = parser.ParseValue(pill, culture); + T? item = parser.ParseValue(pill, culture); if (item != null) { result.Add(item); diff --git a/src/ImageSharp.Web/Commands/Converters/ColorConverter.cs b/src/ImageSharp.Web/Commands/Converters/ColorConverter.cs index eba6b37e..6bab4625 100644 --- a/src/ImageSharp.Web/Commands/Converters/ColorConverter.cs +++ b/src/ImageSharp.Web/Commands/Converters/ColorConverter.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Reflection; @@ -35,7 +34,7 @@ public sealed class ColorConverter : ICommandConverter /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Color ConvertFrom(CommandParser parser, CultureInfo culture, string value, Type propertyType) + public Color ConvertFrom(CommandParser parser, CultureInfo culture, string? value, Type propertyType) { if (string.IsNullOrWhiteSpace(value)) { @@ -60,9 +59,9 @@ public Color ConvertFrom(CommandParser parser, CultureInfo culture, string value if (convert) { - List rgba = parser.ParseValue>(value, culture); + List? rgba = parser.ParseValue>(value, culture); - return rgba.Count switch + return rgba?.Count switch { 4 => Color.FromRgba(rgba[0], rgba[1], rgba[2], rgba[3]), 3 => Color.FromRgb(rgba[0], rgba[1], rgba[2]), @@ -90,7 +89,7 @@ private static IDictionary InitializeColorConstantsTable() { if (field.FieldType == typeof(Color)) { - table[field.Name] = (Color)field.GetValue(null); + table[field.Name] = (Color)field.GetValue(null)!; } } diff --git a/src/ImageSharp.Web/Commands/Converters/EnumConverter.cs b/src/ImageSharp.Web/Commands/Converters/EnumConverter.cs index 764a68a3..5c34505e 100644 --- a/src/ImageSharp.Web/Commands/Converters/EnumConverter.cs +++ b/src/ImageSharp.Web/Commands/Converters/EnumConverter.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Runtime.CompilerServices; @@ -22,10 +21,10 @@ public sealed class EnumConverter : ICommandConverter /// This allows us to reuse the same converter for infinite enum types. /// [MethodImpl(MethodImplOptions.NoInlining)] - public object ConvertFrom( + public object? ConvertFrom( CommandParser parser, CultureInfo culture, - string value, + string? value, Type propertyType) { if (string.IsNullOrWhiteSpace(value)) diff --git a/src/ImageSharp.Web/Commands/Converters/ICommandConverter.cs b/src/ImageSharp.Web/Commands/Converters/ICommandConverter.cs index 7b7e750a..f81c75e5 100644 --- a/src/ImageSharp.Web/Commands/Converters/ICommandConverter.cs +++ b/src/ImageSharp.Web/Commands/Converters/ICommandConverter.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; @@ -35,5 +34,5 @@ public interface ICommandConverter : ICommandConverter /// The to convert. /// The property type that the converter will convert to. /// The conversion cannot be performed. - T ConvertFrom(CommandParser parser, CultureInfo culture, string value, Type propertyType); + T? ConvertFrom(CommandParser parser, CultureInfo culture, string? value, Type propertyType); } diff --git a/src/ImageSharp.Web/Commands/Converters/IntegralNumberConverter{T}.cs b/src/ImageSharp.Web/Commands/Converters/IntegralNumberConverter{T}.cs index 5c49e141..3fb27fb4 100644 --- a/src/ImageSharp.Web/Commands/Converters/IntegralNumberConverter{T}.cs +++ b/src/ImageSharp.Web/Commands/Converters/IntegralNumberConverter{T}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; @@ -20,7 +19,7 @@ public sealed class IntegralNumberConverter : ICommandConverter public T ConvertFrom( CommandParser parser, CultureInfo culture, - string value, + string? value, Type propertyType) { if (string.IsNullOrWhiteSpace(value) diff --git a/src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs b/src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs index 4b2fdd3a..cf4a14a7 100644 --- a/src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs +++ b/src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Runtime.CompilerServices; @@ -21,7 +20,7 @@ public sealed class ListConverter : ICommandConverter> public List ConvertFrom( CommandParser parser, CultureInfo culture, - string value, + string? value, Type propertyType) { var result = new List(); @@ -32,7 +31,7 @@ public List ConvertFrom( foreach (string pill in GetStringArray(value, culture)) { - T item = parser.ParseValue(pill, culture); + T? item = parser.ParseValue(pill, culture); if (item != null) { result.Add(item); diff --git a/src/ImageSharp.Web/Commands/Converters/SimpleCommandConverter{T}.cs b/src/ImageSharp.Web/Commands/Converters/SimpleCommandConverter{T}.cs index d02359a9..399e3120 100644 --- a/src/ImageSharp.Web/Commands/Converters/SimpleCommandConverter{T}.cs +++ b/src/ImageSharp.Web/Commands/Converters/SimpleCommandConverter{T}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Runtime.CompilerServices; @@ -19,10 +18,10 @@ public sealed class SimpleCommandConverter : ICommandConverter /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T ConvertFrom( + public T? ConvertFrom( CommandParser parser, CultureInfo culture, - string value, + string? value, Type propertyType) { if (string.IsNullOrWhiteSpace(value)) @@ -31,7 +30,7 @@ public T ConvertFrom( } Type t = typeof(T); - Type u = Nullable.GetUnderlyingType(t); + Type? u = Nullable.GetUnderlyingType(t); if (u != null) { diff --git a/src/ImageSharp.Web/Processors/FormatWebProcessor.cs b/src/ImageSharp.Web/Processors/FormatWebProcessor.cs index b4e94e9c..f8093018 100644 --- a/src/ImageSharp.Web/Processors/FormatWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/FormatWebProcessor.cs @@ -49,7 +49,7 @@ public FormattedImage Process( CommandParser parser, CultureInfo culture) { - string extension = commands.GetValueOrDefault(Format); + string? extension = commands.GetValueOrDefault(Format); if (!string.IsNullOrWhiteSpace(extension) && this.options.Configuration.ImageFormatsManager.TryFindFormatByFileExtension(extension, out IImageFormat? format)) diff --git a/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs b/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs index dbad7950..7e00ff59 100644 --- a/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs @@ -163,7 +163,12 @@ private static Size ParseSize( CommandParser parser, CultureInfo culture) { - float[] coordinates = parser.ParseValue(commands.GetValueOrDefault(Xy), culture); + float[]? coordinates = parser.ParseValue(commands.GetValueOrDefault(Xy), culture); + + if (coordinates is null) + { + return null; + } if (coordinates.Length != 2) { @@ -198,7 +203,7 @@ private static bool GetCompandMode( private static IResampler GetSampler(CommandCollection commands) { - string sampler = commands.GetValueOrDefault(Sampler); + string? sampler = commands.GetValueOrDefault(Sampler); if (sampler != null) { From 5b6086d261ee6aa1d7c70c23b72f21948a3a0781 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 1 Mar 2023 21:33:38 +0100 Subject: [PATCH 25/34] Remove nullable disable from caching --- src/ImageSharp.Web/Caching/HexEncoder.cs | 1 - src/ImageSharp.Web/Caching/ICacheHash.cs | 1 - src/ImageSharp.Web/Caching/ICacheKey.cs | 1 - src/ImageSharp.Web/Caching/IImageCache.cs | 3 +-- .../Caching/LegacyV1CacheKey.cs | 1 - .../ConcurrentTLruCache{TKey,TValue}.cs | 25 ++++++++++--------- .../LongTickCountLruItem{TKey,TValue}.cs | 1 - .../TLruLongTicksPolicy{TKey,TValue}.cs | 1 - .../Caching/PhysicalFileSystemCache.cs | 11 ++++---- .../Caching/PhysicalFileSystemCacheOptions.cs | 3 +-- src/ImageSharp.Web/Caching/SHA256CacheHash.cs | 3 +-- .../Caching/UriAbsoluteCacheKey.cs | 1 - .../UriAbsoluteLowerInvariantCacheKey.cs | 1 - .../Caching/UriRelativeCacheKey.cs | 1 - .../UriRelativeLowerInvariantCacheKey.cs | 1 - 15 files changed, 21 insertions(+), 34 deletions(-) diff --git a/src/ImageSharp.Web/Caching/HexEncoder.cs b/src/ImageSharp.Web/Caching/HexEncoder.cs index 7687cc3f..acf72787 100644 --- a/src/ImageSharp.Web/Caching/HexEncoder.cs +++ b/src/ImageSharp.Web/Caching/HexEncoder.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/ImageSharp.Web/Caching/ICacheHash.cs b/src/ImageSharp.Web/Caching/ICacheHash.cs index bf262d0e..26324709 100644 --- a/src/ImageSharp.Web/Caching/ICacheHash.cs +++ b/src/ImageSharp.Web/Caching/ICacheHash.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Web.Caching; diff --git a/src/ImageSharp.Web/Caching/ICacheKey.cs b/src/ImageSharp.Web/Caching/ICacheKey.cs index 2a23d42a..d7d3ae43 100644 --- a/src/ImageSharp.Web/Caching/ICacheKey.cs +++ b/src/ImageSharp.Web/Caching/ICacheKey.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Http; using SixLabors.ImageSharp.Web.Commands; diff --git a/src/ImageSharp.Web/Caching/IImageCache.cs b/src/ImageSharp.Web/Caching/IImageCache.cs index bdd22c6b..1ac8e5cd 100644 --- a/src/ImageSharp.Web/Caching/IImageCache.cs +++ b/src/ImageSharp.Web/Caching/IImageCache.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using SixLabors.ImageSharp.Web.Resolvers; @@ -17,7 +16,7 @@ public interface IImageCache /// /// The cache key. /// The . - Task GetAsync(string key); + Task GetAsync(string key); /// /// Sets the value associated with the specified key. diff --git a/src/ImageSharp.Web/Caching/LegacyV1CacheKey.cs b/src/ImageSharp.Web/Caching/LegacyV1CacheKey.cs index e3abe213..64b56e0b 100644 --- a/src/ImageSharp.Web/Caching/LegacyV1CacheKey.cs +++ b/src/ImageSharp.Web/Caching/LegacyV1CacheKey.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Text; diff --git a/src/ImageSharp.Web/Caching/LruCache/ConcurrentTLruCache{TKey,TValue}.cs b/src/ImageSharp.Web/Caching/LruCache/ConcurrentTLruCache{TKey,TValue}.cs index 8e9e7494..dbb1cf16 100644 --- a/src/ImageSharp.Web/Caching/LruCache/ConcurrentTLruCache{TKey,TValue}.cs +++ b/src/ImageSharp.Web/Caching/LruCache/ConcurrentTLruCache{TKey,TValue}.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Web.Caching; @@ -26,6 +26,7 @@ namespace SixLabors.ImageSharp.Web.Caching; /// 6. When cold is full, cold tail is moved to warm head or removed from dictionary on depending on WasAccessed. /// internal class ConcurrentTLruCache + where TKey : notnull { private readonly ConcurrentDictionary> dictionary; @@ -97,9 +98,9 @@ public ConcurrentTLruCache(int concurrencyLevel, int capacity, IEqualityComparer /// The key of the value to get. /// When this method returns, contains the object from the cache that has the specified key, or the default value of the type if the operation failed. /// if the key was found in the cache; otherwise, . - public bool TryGet(TKey key, out TValue value) + public bool TryGet(TKey key, [NotNullWhen(true)] out TValue? value) { - if (this.dictionary.TryGetValue(key, out LongTickCountLruItem item)) + if (this.dictionary.TryGetValue(key, out LongTickCountLruItem? item)) { return this.GetOrDiscard(item, out value); } @@ -111,7 +112,7 @@ public bool TryGet(TKey key, out TValue value) // AggressiveInlining forces the JIT to inline policy.ShouldDiscard(). For LRU policy // the first branch is completely eliminated due to JIT time constant propogation. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool GetOrDiscard(LongTickCountLruItem item, out TValue value) + private bool GetOrDiscard(LongTickCountLruItem item, out TValue? value) { if (this.policy.ShouldDiscard(item)) { @@ -135,7 +136,7 @@ private bool GetOrDiscard(LongTickCountLruItem item, out TValue va /// in the cache, or the new value if the key was not in the dictionary. public TValue GetOrAdd(TKey key, Func valueFactory) { - if (this.TryGet(key, out TValue value)) + if (this.TryGet(key, out TValue? value)) { return value; } @@ -164,7 +165,7 @@ public TValue GetOrAdd(TKey key, Func valueFactory) /// A task that represents the asynchronous operation. public async Task GetOrAddAsync(TKey key, Func> valueFactory) { - if (this.TryGet(key, out TValue value)) + if (this.TryGet(key, out TValue? value)) { return value; } @@ -202,7 +203,7 @@ public bool TryRemove(TKey key) // and it will not be marked as removed. If key 1 is fetched while LruItem1* is still in the queue, there will // be two queue entries for key 1, and neither is marked as removed. Thus when LruItem1 * ages out, it will // incorrectly remove 1 from the dictionary, and this cycle can repeat. - if (this.dictionary.TryGetValue(key, out LongTickCountLruItem existing)) + if (this.dictionary.TryGetValue(key, out LongTickCountLruItem? existing)) { if (existing.WasRemoved) { @@ -219,7 +220,7 @@ public bool TryRemove(TKey key) existing.WasRemoved = true; } - if (this.dictionary.TryRemove(key, out LongTickCountLruItem removedItem)) + if (this.dictionary.TryRemove(key, out LongTickCountLruItem? removedItem)) { // Mark as not accessed, it will later be cycled out of the queues because it can never be fetched // from the dictionary. Note: Hot/Warm/Cold count will reflect the removed item until it is cycled @@ -261,7 +262,7 @@ private void CycleHot() { Interlocked.Decrement(ref this.hotCount); - if (this.hotQueue.TryDequeue(out LongTickCountLruItem item)) + if (this.hotQueue.TryDequeue(out LongTickCountLruItem? item)) { ItemDestination where = this.policy.RouteHot(item); this.Move(item, where); @@ -279,7 +280,7 @@ private void CycleWarm() { Interlocked.Decrement(ref this.warmCount); - if (this.warmQueue.TryDequeue(out LongTickCountLruItem item)) + if (this.warmQueue.TryDequeue(out LongTickCountLruItem? item)) { ItemDestination where = this.policy.RouteWarm(item); @@ -308,7 +309,7 @@ private void CycleCold() { Interlocked.Decrement(ref this.coldCount); - if (this.coldQueue.TryDequeue(out LongTickCountLruItem item)) + if (this.coldQueue.TryDequeue(out LongTickCountLruItem? item)) { ItemDestination where = this.policy.RouteCold(item); @@ -354,7 +355,7 @@ private void Move(LongTickCountLruItem item, ItemDestination where break; } - if (this.dictionary.TryRemove(item.Key, out LongTickCountLruItem removedItem)) + if (this.dictionary.TryRemove(item.Key, out LongTickCountLruItem? removedItem)) { item.WasRemoved = true; if (removedItem.Value is IDisposable d) diff --git a/src/ImageSharp.Web/Caching/LruCache/LongTickCountLruItem{TKey,TValue}.cs b/src/ImageSharp.Web/Caching/LruCache/LongTickCountLruItem{TKey,TValue}.cs index 7bd0a76d..58cb46eb 100644 --- a/src/ImageSharp.Web/Caching/LruCache/LongTickCountLruItem{TKey,TValue}.cs +++ b/src/ImageSharp.Web/Caching/LruCache/LongTickCountLruItem{TKey,TValue}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Web.Caching; diff --git a/src/ImageSharp.Web/Caching/LruCache/TLruLongTicksPolicy{TKey,TValue}.cs b/src/ImageSharp.Web/Caching/LruCache/TLruLongTicksPolicy{TKey,TValue}.cs index 30f28b4f..d6994ec5 100644 --- a/src/ImageSharp.Web/Caching/LruCache/TLruLongTicksPolicy{TKey,TValue}.cs +++ b/src/ImageSharp.Web/Caching/LruCache/TLruLongTicksPolicy{TKey,TValue}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Diagnostics; using System.Runtime.CompilerServices; diff --git a/src/ImageSharp.Web/Caching/PhysicalFileSystemCache.cs b/src/ImageSharp.Web/Caching/PhysicalFileSystemCache.cs index ff0847e0..34fe94a3 100644 --- a/src/ImageSharp.Web/Caching/PhysicalFileSystemCache.cs +++ b/src/ImageSharp.Web/Caching/PhysicalFileSystemCache.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -77,17 +76,17 @@ internal static string GetCacheRoot(PhysicalFileSystemCacheOptions cacheOptions, } /// - public Task GetAsync(string key) + public Task GetAsync(string key) { string path = Path.Combine(this.cacheRootPath, ToFilePath(key, this.cacheFolderDepth)); FileInfo metaFileInfo = new(ToMetaDataFilePath(path)); if (!metaFileInfo.Exists) { - return Task.FromResult(null); + return Task.FromResult(null); } - return Task.FromResult(new PhysicalFileSystemCacheResolver(metaFileInfo, this.formatUtilities)); + return Task.FromResult(new PhysicalFileSystemCacheResolver(metaFileInfo, this.formatUtilities)); } /// @@ -96,10 +95,10 @@ public async Task SetAsync(string key, Stream stream, ImageCacheMetadata metadat string path = Path.Combine(this.cacheRootPath, ToFilePath(key, this.cacheFolderDepth)); string imagePath = this.ToImageFilePath(path, metadata); string metaPath = ToMetaDataFilePath(path); - string directory = Path.GetDirectoryName(path); + string? directory = Path.GetDirectoryName(path); // Ensure cache directory is created before creating files - if (!Directory.Exists(directory)) + if (!Directory.Exists(directory) && directory is not null) { Directory.CreateDirectory(directory); } diff --git a/src/ImageSharp.Web/Caching/PhysicalFileSystemCacheOptions.cs b/src/ImageSharp.Web/Caching/PhysicalFileSystemCacheOptions.cs index ad09f177..ec2d2410 100644 --- a/src/ImageSharp.Web/Caching/PhysicalFileSystemCacheOptions.cs +++ b/src/ImageSharp.Web/Caching/PhysicalFileSystemCacheOptions.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable namespace SixLabors.ImageSharp.Web.Caching; @@ -21,7 +20,7 @@ public class PhysicalFileSystemCacheOptions /// application content files; commonly 'wwwroot'. /// /// - public string CacheRootPath { get; set; } + public string? CacheRootPath { get; set; } /// /// Gets or sets the cache folder name. diff --git a/src/ImageSharp.Web/Caching/SHA256CacheHash.cs b/src/ImageSharp.Web/Caching/SHA256CacheHash.cs index f0d3f16d..7eeae17a 100644 --- a/src/ImageSharp.Web/Caching/SHA256CacheHash.cs +++ b/src/ImageSharp.Web/Caching/SHA256CacheHash.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Runtime.CompilerServices; @@ -33,7 +32,7 @@ public SHA256CacheHash(IOptions options) public string Create(string value, uint length) { int byteCount = Encoding.ASCII.GetByteCount(value); - byte[] buffer = null; + byte[]? buffer = null; try { diff --git a/src/ImageSharp.Web/Caching/UriAbsoluteCacheKey.cs b/src/ImageSharp.Web/Caching/UriAbsoluteCacheKey.cs index 74ed4c59..ea9f126a 100644 --- a/src/ImageSharp.Web/Caching/UriAbsoluteCacheKey.cs +++ b/src/ImageSharp.Web/Caching/UriAbsoluteCacheKey.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Http; using SixLabors.ImageSharp.Web.Commands; diff --git a/src/ImageSharp.Web/Caching/UriAbsoluteLowerInvariantCacheKey.cs b/src/ImageSharp.Web/Caching/UriAbsoluteLowerInvariantCacheKey.cs index 1c78b30f..2e3069d4 100644 --- a/src/ImageSharp.Web/Caching/UriAbsoluteLowerInvariantCacheKey.cs +++ b/src/ImageSharp.Web/Caching/UriAbsoluteLowerInvariantCacheKey.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Http; using SixLabors.ImageSharp.Web.Commands; diff --git a/src/ImageSharp.Web/Caching/UriRelativeCacheKey.cs b/src/ImageSharp.Web/Caching/UriRelativeCacheKey.cs index cefe8dd0..01d08740 100644 --- a/src/ImageSharp.Web/Caching/UriRelativeCacheKey.cs +++ b/src/ImageSharp.Web/Caching/UriRelativeCacheKey.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Http; using SixLabors.ImageSharp.Web.Commands; diff --git a/src/ImageSharp.Web/Caching/UriRelativeLowerInvariantCacheKey.cs b/src/ImageSharp.Web/Caching/UriRelativeLowerInvariantCacheKey.cs index 1071c333..77e423eb 100644 --- a/src/ImageSharp.Web/Caching/UriRelativeLowerInvariantCacheKey.cs +++ b/src/ImageSharp.Web/Caching/UriRelativeLowerInvariantCacheKey.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using Microsoft.AspNetCore.Http; using SixLabors.ImageSharp.Web.Commands; From 603f3e53c3a02bc066d57c145b82243073e98dd9 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 3 Mar 2023 10:44:29 +0100 Subject: [PATCH 26/34] Remove x + y from targetrectangle --- .../Processors/ResizeWebProcessor.cs | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs b/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs index f85447a7..b94d436d 100644 --- a/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs @@ -62,16 +62,6 @@ public class ResizeWebProcessor : IImageWebProcessor /// public const string Compand = "compand"; - /// - /// The command constant for the resize TargetRectangle X - /// - public const string TargetRectangleX = "targetrectanglex"; - - /// - /// The command constant for the resize TargetRectangle X - /// - public const string TargetRectangleY = "targetrectangley"; - /// /// The command constant for the resize TargetRectangle X /// @@ -196,15 +186,7 @@ private static Size ParseSize( int width = (int)parser.ParseValue(commands.GetValueOrDefault(TargetRectangleWidth), culture); int height = (int)parser.ParseValue(commands.GetValueOrDefault(TargetRectangleHeight), culture); - if (!commands.Contains(TargetRectangleX) || !commands.Contains(TargetRectangleY)) - { - return new Rectangle(0, 0, width, height); - } - - int x = (int)parser.ParseValue(commands.GetValueOrDefault(TargetRectangleX), culture); - int y = (int)parser.ParseValue(commands.GetValueOrDefault(TargetRectangleY), culture); - - return new Rectangle(x, y, width, height); + return new Rectangle(0, 0, width, height); } private static PointF? GetCenter( From e83c10cba1665d31c72dbdc9217aa59eda274243 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 3 Mar 2023 15:20:55 +0100 Subject: [PATCH 27/34] Only create rectangle when mode = manual --- .../Processors/ResizeWebProcessor.cs | 33 ++----------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs b/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs index b94d436d..80956798 100644 --- a/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs +++ b/src/ImageSharp.Web/Processors/ResizeWebProcessor.cs @@ -62,16 +62,6 @@ public class ResizeWebProcessor : IImageWebProcessor /// public const string Compand = "compand"; - /// - /// The command constant for the resize TargetRectangle X - /// - public const string TargetRectangleWidth = "targetrectanglewidth"; - - /// - /// The command constant for the resize TargetRectangle X - /// - public const string TargetRectangleHeight = "targetrectangleheight"; - private static readonly IEnumerable ResizeCommands = new[] { @@ -137,18 +127,18 @@ internal static ResizeOptions GetResizeOptions( return null; } - Rectangle? targetRectangle = ParseRectangle(commands, parser, culture); + ResizeMode mode = GetMode(commands, parser, culture); return new() { Size = size, CenterCoordinates = GetCenter(orientation, commands, parser, culture), Position = GetAnchor(orientation, commands, parser, culture), - Mode = GetMode(commands, parser, culture), + Mode = mode, Compand = GetCompandMode(commands, parser, culture), Sampler = GetSampler(commands), PadColor = parser.ParseValue(commands.GetValueOrDefault(Color), culture), - TargetRectangle = targetRectangle + TargetRectangle = mode is ResizeMode.Manual ? new Rectangle(0, 0, size.Width, size.Height) : null }; } @@ -172,23 +162,6 @@ private static Size ParseSize( return ExifOrientationUtilities.Transform(new Size(width, height), orientation); } - private static Rectangle? ParseRectangle( - CommandCollection commands, - CommandParser parser, - CultureInfo culture) - { - if (!commands.Contains(TargetRectangleWidth) || !commands.Contains(TargetRectangleHeight)) - { - return null; - } - - // The command parser will reject negative numbers as it clamps values to ranges. - int width = (int)parser.ParseValue(commands.GetValueOrDefault(TargetRectangleWidth), culture); - int height = (int)parser.ParseValue(commands.GetValueOrDefault(TargetRectangleHeight), culture); - - return new Rectangle(0, 0, width, height); - } - private static PointF? GetCenter( ushort orientation, CommandCollection commands, From f349ad19dad6ddff1e127cc8491dc0549889df52 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Mar 2023 16:16:44 +1000 Subject: [PATCH 28/34] Cleanup return --- src/ImageSharp.Web/Commands/CommandParser.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp.Web/Commands/CommandParser.cs b/src/ImageSharp.Web/Commands/CommandParser.cs index 19704159..4bd0f534 100644 --- a/src/ImageSharp.Web/Commands/CommandParser.cs +++ b/src/ImageSharp.Web/Commands/CommandParser.cs @@ -67,14 +67,10 @@ public CommandParser(IEnumerable converters) } } - // We don't actually return here. - // The compiler just cannot see our exception. - ThrowNotSupported(type); - return default; + return ThrowNotSupported(type); } - [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] - private static void ThrowNotSupported(Type type) + private static T ThrowNotSupported(Type type) => throw new NotSupportedException($"Cannot convert to {type.FullName}."); } From 5ee6d62cc8f92fda2d139015a71a404d0ce29e6a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Mar 2023 16:18:49 +1000 Subject: [PATCH 29/34] Optimize ColorConverter --- .../Commands/Converters/ColorConverter.cs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp.Web/Commands/Converters/ColorConverter.cs b/src/ImageSharp.Web/Commands/Converters/ColorConverter.cs index 6bab4625..6d21a828 100644 --- a/src/ImageSharp.Web/Commands/Converters/ColorConverter.cs +++ b/src/ImageSharp.Web/Commands/Converters/ColorConverter.cs @@ -41,9 +41,21 @@ public Color ConvertFrom(CommandParser parser, CultureInfo culture, string? valu return default; } + // Hex colors rgb, rrggbb, rrggbbaa + if (HexColorRegex.IsMatch(value)) + { + return Color.ParseHex(value); + } + + // Named colors + IDictionary table = ColorConstantsTable.Value; + if (table.TryGetValue(value, out Color color)) + { + return color; + } + // Numeric r,g,b - r,g,b,a char separator = culture.TextInfo.ListSeparator[0]; - if (value.IndexOf(separator) != -1) { string[] components = value.Split(separator); @@ -70,15 +82,7 @@ public Color ConvertFrom(CommandParser parser, CultureInfo culture, string? valu } } - // Hex colors rgb, rrggbb, rrggbbaa - if (HexColorRegex.IsMatch(value)) - { - return Color.ParseHex(value); - } - - // Named colors - IDictionary table = ColorConstantsTable.Value; - return table.TryGetValue(value, out Color color) ? color : default; + return default; } private static IDictionary InitializeColorConstantsTable() From 885e25948154c42d203c1e890ff314bd7b634bcd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Mar 2023 16:23:32 +1000 Subject: [PATCH 30/34] Cleanup --- src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs | 4 ++-- src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs b/src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs index bb7e70c5..58f7f835 100644 --- a/src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs +++ b/src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs @@ -28,11 +28,11 @@ public T[] ConvertFrom( return Array.Empty(); } - var result = new List(); + List result = new(); foreach (string pill in GetStringArray(value, culture)) { T? item = parser.ParseValue(pill, culture); - if (item != null) + if (item is not null) { result.Add(item); } diff --git a/src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs b/src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs index cf4a14a7..a390b78e 100644 --- a/src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs +++ b/src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs @@ -23,7 +23,7 @@ public List ConvertFrom( string? value, Type propertyType) { - var result = new List(); + List result = new(); if (string.IsNullOrWhiteSpace(value)) { return result; @@ -32,7 +32,7 @@ public List ConvertFrom( foreach (string pill in GetStringArray(value, culture)) { T? item = parser.ParseValue(pill, culture); - if (item != null) + if (item is not null) { result.Add(item); } From 2821506d55916dde02cb91d7665166dc3b2644bf Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Mar 2023 21:16:17 +1000 Subject: [PATCH 31/34] .NET 7 fixes Update ImageSharp.Web.csproj --- .../Commands/PresetOnlyQueryCollectionRequestParser.cs | 10 +++++++--- .../Commands/QueryCollectionRequestParser.cs | 6 +++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp.Web/Commands/PresetOnlyQueryCollectionRequestParser.cs b/src/ImageSharp.Web/Commands/PresetOnlyQueryCollectionRequestParser.cs index 15f7a4e1..6386ee7d 100644 --- a/src/ImageSharp.Web/Commands/PresetOnlyQueryCollectionRequestParser.cs +++ b/src/ImageSharp.Web/Commands/PresetOnlyQueryCollectionRequestParser.cs @@ -40,8 +40,8 @@ public CommandCollection ParseRequestCommands(HttpContext context) } StringValues query = queryCollection[QueryKey]; - string requestedPreset = query[query.Count - 1]; - if (this.presets.TryGetValue(requestedPreset, out CommandCollection? collection)) + string? requestedPreset = query[^1]; + if (requestedPreset is not null && this.presets.TryGetValue(requestedPreset, out CommandCollection? collection)) { return collection; } @@ -65,7 +65,11 @@ private static CommandCollection ParsePreset(string unparsedPresetValue) foreach (KeyValuePair pair in parsed) { // Use the indexer for both set and query. This replaces any previously parsed values. - transformed[pair.Key] = pair.Value[pair.Value.Count - 1]; + string? value = pair.Value[^1]; + if (value is not null) + { + transformed[pair.Key] = value; + } } return transformed; diff --git a/src/ImageSharp.Web/Commands/QueryCollectionRequestParser.cs b/src/ImageSharp.Web/Commands/QueryCollectionRequestParser.cs index c4ac88c9..420fc019 100644 --- a/src/ImageSharp.Web/Commands/QueryCollectionRequestParser.cs +++ b/src/ImageSharp.Web/Commands/QueryCollectionRequestParser.cs @@ -25,7 +25,11 @@ public CommandCollection ParseRequestCommands(HttpContext context) foreach (KeyValuePair pair in query) { // Use the indexer for both set and query. This replaces any previously parsed values. - transformed[pair.Key] = pair.Value[pair.Value.Count - 1]; + string? value = pair.Value[^1]; + if (value is not null) + { + transformed[pair.Key] = value; + } } return transformed; From 5535823f29cb2166d3e3cbf7f21ab6e23a0fc5fa Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Mar 2023 21:26:26 +1000 Subject: [PATCH 32/34] Fix last nullable issues --- src/ImageSharp.Web/FormattedImage.cs | 19 +++++++++---------- .../Providers/FileProviderImageProvider.cs | 12 ++++++++---- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/ImageSharp.Web/FormattedImage.cs b/src/ImageSharp.Web/FormattedImage.cs index 0ea5abbb..65c82df2 100644 --- a/src/ImageSharp.Web/FormattedImage.cs +++ b/src/ImageSharp.Web/FormattedImage.cs @@ -1,8 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable -using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -40,14 +39,15 @@ private FormattedImage(Image image, IImageFormat format, bool keepOpen) { this.Image = image; this.imageFormatsManager = image.GetConfiguration().ImageFormatsManager; - this.Format = format; + this.format = format; + this.encoder = this.imageFormatsManager.GetEncoder(format); this.keepOpen = keepOpen; } /// /// Gets the decoded image. /// - public Image Image { get; private set; } + public Image Image { get; } /// /// Gets or sets the format. @@ -105,7 +105,7 @@ internal static async Task LoadAsync(DecoderOptions opti // For example. If a resize command has been passed with no extra resampling options // then we should apply those changes on decode. This will allow memory savings and performance improvements. Image image = await Image.LoadAsync(options, source); - return new FormattedImage(image, image.Metadata.DecodedImageFormat, false); + return new FormattedImage(image, image.Metadata.DecodedImageFormat!, false); } /// @@ -117,7 +117,7 @@ internal static async Task LoadAsync(DecoderOptions opti internal static async Task LoadAsync(DecoderOptions options, Stream source) { Image image = await Image.LoadAsync(options, source); - return new FormattedImage(image, image.Metadata.DecodedImageFormat, false); + return new FormattedImage(image, image.Metadata.DecodedImageFormat!, false); } /// @@ -143,7 +143,7 @@ public bool TryGetExifOrientation(out ushort value) value = ExifOrientationMode.Unknown; if (this.Image.Metadata.ExifProfile != null) { - if (!this.Image.Metadata.ExifProfile.TryGetValue(ExifTag.Orientation, out IExifValue orientation)) + if (!this.Image.Metadata.ExifProfile.TryGetValue(ExifTag.Orientation, out IExifValue? orientation)) { return false; } @@ -172,13 +172,12 @@ public void Dispose() if (!this.keepOpen) { this.Image?.Dispose(); - this.Image = null; } } - [MethodImpl(MethodImplOptions.NoInlining)] + [DoesNotReturn] private static void ThrowNull(string name) => throw new ArgumentNullException(name); - [MethodImpl(MethodImplOptions.NoInlining)] + [DoesNotReturn] private static void ThrowInvalid(string name) => throw new ArgumentException(name); } diff --git a/src/ImageSharp.Web/Providers/FileProviderImageProvider.cs b/src/ImageSharp.Web/Providers/FileProviderImageProvider.cs index 2e0cb6d4..96f55441 100644 --- a/src/ImageSharp.Web/Providers/FileProviderImageProvider.cs +++ b/src/ImageSharp.Web/Providers/FileProviderImageProvider.cs @@ -52,12 +52,16 @@ public virtual bool IsValidRequest(HttpContext context) /// public Task GetAsync(HttpContext context) { - IFileInfo fileInfo = this.fileProvider.GetFileInfo(context.Request.Path.Value); - if (!fileInfo.Exists) + string? path = context.Request.Path.Value; + if (path is not null) { - return Task.FromResult(null); + IFileInfo fileInfo = this.fileProvider.GetFileInfo(path); + if (fileInfo.Exists) + { + return Task.FromResult(new FileProviderImageResolver(fileInfo)); + } } - return Task.FromResult(new FileProviderImageResolver(fileInfo)); + return Task.FromResult(null); } } From 40529581d255baa79b2616a98c6de2fb26f6cc93 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Mar 2023 21:29:08 +1000 Subject: [PATCH 33/34] Fix readme and workflow --- .github/workflows/build-and-test.yml | 14 ++++++++++---- README.md | 6 +++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 97aafb97..78220623 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -189,9 +189,15 @@ jobs: shell: pwsh run: ./ci-pack.ps1 - - name: MyGet Publish + - name: Feedz Publish shell: pwsh run: | - dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v2/package - dotnet nuget push .\artifacts\*.snupkg -k ${{secrets.MYGET_TOKEN}} -s https://www.myget.org/F/sixlabors/api/v3/index.json - # TODO: If github.ref starts with 'refs/tags' then it was tag push and we can optionally push out package to nuget.org + dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.FEEDZ_TOKEN}} -s https://f.feedz.io/sixlabors/sixlabors/nuget/index.json --skip-duplicate + dotnet nuget push .\artifacts\*.snupkg -k ${{secrets.FEEDZ_TOKEN}} -s https://f.feedz.io/sixlabors/sixlabors/symbols --skip-duplicate + + - name: NuGet Publish + if: ${{ startsWith(github.ref, 'refs/tags/') }} + shell: pwsh + run: | + dotnet nuget push .\artifacts\*.nupkg -k ${{secrets.NUGET_TOKEN}} -s https://api.nuget.org/v3/index.json --skip-duplicate + dotnet nuget push .\artifacts\*.snupkg -k ${{secrets.NUGET_TOKEN}} -s https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/README.md b/README.md index 0ded0990..189b9788 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,11 @@ For more information, see the [.NET Foundation Code of Conduct](https://dotnetfo ### Installation -Install stable releases via Nuget; development releases are available via MyGet. +Install stable releases via Nuget; development releases are available via Feedz.io. -| Package Name | Release (NuGet) | Nightly (MyGet) | +| Package Name | Release (NuGet) | Nightly (Feedz.io) | |--------------------------------|-----------------|-----------------| -| `SixLabors.ImageSharp.Web` | [![NuGet](https://img.shields.io/nuget/v/SixLabors.ImageSharp.Web.svg)](https://www.nuget.org/packages/SixLabors.ImageSharp.Web/) | [![MyGet](https://img.shields.io/myget/sixlabors/vpre/SixLabors.ImageSharp.Web.svg)](https://www.myget.org/feed/sixlabors/package/nuget/SixLabors.ImageSharp.Web) | +| `SixLabors.ImageSharp.Web` | [![NuGet](https://img.shields.io/nuget/v/SixLabors.ImageSharp.Web.svg)](https://www.nuget.org/packages/SixLabors.ImageSharp.Web/) | [![feedz.io](https://img.shields.io/badge/endpoint.svg?url=https%3A%2F%2Ff.feedz.io%2Fsixlabors%2Fsixlabors%2Fshield%2FSixLabors.ImageSharp.Web%2Flatest)](https://f.feedz.io/sixlabors/sixlabors/nuget/index.json) | ## Manual build From bc1e92372d6a678883dcf013fe4d9ea7ac33c0c0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Mar 2023 21:55:42 +1000 Subject: [PATCH 34/34] Fix color converter --- .../Commands/Converters/ArrayConverter{T}.cs | 5 ++++- .../Commands/Converters/ColorConverter.cs | 16 ++++++++-------- .../Commands/Converters/ConverterUtility.cs | 19 +++++++++++++++++++ .../Commands/Converters/EnumConverter.cs | 5 ++++- .../Commands/Converters/ListConverter{T}.cs | 5 ++++- 5 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 src/ImageSharp.Web/Commands/Converters/ConverterUtility.cs diff --git a/src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs b/src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs index 58f7f835..426da7e3 100644 --- a/src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs +++ b/src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs @@ -44,7 +44,10 @@ public T[] ConvertFrom( [MethodImpl(MethodImplOptions.AggressiveInlining)] private static string[] GetStringArray(string input, CultureInfo culture) { - char separator = culture.TextInfo.ListSeparator[0]; + char separator = ConverterUtility.GetListSeparator(culture); + + // TODO: Can we use StringSplit Enumerator here? + // https://github.com/dotnet/runtime/issues/934 return input.Split(separator).Select(s => s.Trim()).ToArray(); } } diff --git a/src/ImageSharp.Web/Commands/Converters/ColorConverter.cs b/src/ImageSharp.Web/Commands/Converters/ColorConverter.cs index 6d21a828..31a1fd67 100644 --- a/src/ImageSharp.Web/Commands/Converters/ColorConverter.cs +++ b/src/ImageSharp.Web/Commands/Converters/ColorConverter.cs @@ -17,7 +17,7 @@ public sealed class ColorConverter : ICommandConverter /// The web color hexadecimal regex. Matches strings arranged /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. /// - private static readonly Regex HexColorRegex = new("([0-9a-fA-F]{3}){1,2}", RegexOptions.Compiled); + private static readonly Regex HexColorRegex = new("([0-9a-fA-F][^,;.-]\\B{3}){1,2}", RegexOptions.Compiled); /// /// The number color regex. @@ -41,12 +41,6 @@ public Color ConvertFrom(CommandParser parser, CultureInfo culture, string? valu return default; } - // Hex colors rgb, rrggbb, rrggbbaa - if (HexColorRegex.IsMatch(value)) - { - return Color.ParseHex(value); - } - // Named colors IDictionary table = ColorConstantsTable.Value; if (table.TryGetValue(value, out Color color)) @@ -55,7 +49,7 @@ public Color ConvertFrom(CommandParser parser, CultureInfo culture, string? valu } // Numeric r,g,b - r,g,b,a - char separator = culture.TextInfo.ListSeparator[0]; + char separator = ConverterUtility.GetListSeparator(culture); if (value.IndexOf(separator) != -1) { string[] components = value.Split(separator); @@ -82,6 +76,12 @@ public Color ConvertFrom(CommandParser parser, CultureInfo culture, string? valu } } + // Hex colors rgb, rrggbb, rrggbbaa + if (HexColorRegex.IsMatch(value)) + { + return Color.ParseHex(value); + } + return default; } diff --git a/src/ImageSharp.Web/Commands/Converters/ConverterUtility.cs b/src/ImageSharp.Web/Commands/Converters/ConverterUtility.cs new file mode 100644 index 00000000..c924e35c --- /dev/null +++ b/src/ImageSharp.Web/Commands/Converters/ConverterUtility.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Web.Commands.Converters; +internal static class ConverterUtility +{ + /// + /// Gets the that is used by the given culture to separate items in a list. + /// + /// The culture. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static char GetListSeparator(CultureInfo culture) + => MemoryMarshal.GetReference(culture.TextInfo.ListSeparator); +} diff --git a/src/ImageSharp.Web/Commands/Converters/EnumConverter.cs b/src/ImageSharp.Web/Commands/Converters/EnumConverter.cs index 5c34505e..b74ae4d5 100644 --- a/src/ImageSharp.Web/Commands/Converters/EnumConverter.cs +++ b/src/ImageSharp.Web/Commands/Converters/EnumConverter.cs @@ -34,7 +34,7 @@ public sealed class EnumConverter : ICommandConverter try { - char separator = culture.TextInfo.ListSeparator[0]; + char separator = ConverterUtility.GetListSeparator(culture); if (value.IndexOf(separator) != -1) { long convertedValue = 0; @@ -63,5 +63,8 @@ public sealed class EnumConverter : ICommandConverter /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] private static string[] GetStringArray(string input, char separator) + + // TODO: Can we use StringSplit Enumerator here? + // https://github.com/dotnet/runtime/issues/934 => input.Split(separator).Select(s => s.Trim()).ToArray(); } diff --git a/src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs b/src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs index a390b78e..5a1e35ca 100644 --- a/src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs +++ b/src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs @@ -44,7 +44,10 @@ public List ConvertFrom( [MethodImpl(MethodImplOptions.AggressiveInlining)] private static string[] GetStringArray(string input, CultureInfo culture) { - char separator = culture.TextInfo.ListSeparator[0]; + char separator = ConverterUtility.GetListSeparator(culture); + + // TODO: Can we use StringSplit Enumerator here? + // https://github.com/dotnet/runtime/issues/934 return input.Split(separator).Select(s => s.Trim()).ToArray(); } }