diff --git a/backend/src/Squidex.Infrastructure/Assets/ImageSharp/ImageSharpAssetThumbnailGenerator.cs b/backend/src/Squidex.Infrastructure/Assets/ImageSharp/ImageSharpAssetThumbnailGenerator.cs index 2c44d9340..cfcb8e9ca 100644 --- a/backend/src/Squidex.Infrastructure/Assets/ImageSharp/ImageSharpAssetThumbnailGenerator.cs +++ b/backend/src/Squidex.Infrastructure/Assets/ImageSharp/ImageSharpAssetThumbnailGenerator.cs @@ -7,6 +7,7 @@ using System; using System.IO; +using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Jpeg; @@ -19,76 +20,89 @@ namespace Squidex.Infrastructure.Assets.ImageSharp { public sealed class ImageSharpAssetThumbnailGenerator : IAssetThumbnailGenerator { - public Task CreateThumbnailAsync(Stream source, Stream destination, ResizeOptions options) + private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(Math.Max(Environment.ProcessorCount / 4, 1)); + + public async Task CreateThumbnailAsync(Stream source, Stream destination, ResizeOptions options) { + Guard.NotNull(source, nameof(source)); + Guard.NotNull(destination, nameof(destination)); Guard.NotNull(options, nameof(options)); if (!options.IsValid) { source.CopyTo(destination); - return Task.CompletedTask; + return; } var w = options.Width ?? 0; var h = options.Height ?? 0; - using (var image = Image.Load(source, out var format)) - { - var encoder = Configuration.Default.ImageFormatsManager.FindEncoder(format); - - if (encoder == null) - { - throw new NotSupportedException(); - } - - if (options.Quality.HasValue && (encoder is JpegEncoder || !options.KeepFormat)) - { - encoder = new JpegEncoder { Quality = options.Quality.Value }; - } + await semaphoreSlim.WaitAsync(); - image.Mutate(x => x.AutoOrient()); - - if (w > 0 || h > 0) + try + { + using (var image = Image.Load(source, out var format)) { - var isCropUpsize = options.Mode == ResizeMode.CropUpsize; + var encoder = Configuration.Default.ImageFormatsManager.FindEncoder(format); - if (!Enum.TryParse(options.Mode.ToString(), true, out var resizeMode)) + if (encoder == null) { - resizeMode = ISResizeMode.Max; + throw new NotSupportedException(); } - if (isCropUpsize) + if (options.Quality.HasValue && (encoder is JpegEncoder || !options.KeepFormat)) { - resizeMode = ISResizeMode.Crop; + encoder = new JpegEncoder { Quality = options.Quality.Value }; } - if (w >= image.Width && h >= image.Height && resizeMode == ISResizeMode.Crop && !isCropUpsize) - { - resizeMode = ISResizeMode.BoxPad; - } - - var resizeOptions = new ISResizeOptions { Size = new Size(w, h), Mode = resizeMode }; + image.Mutate(x => x.AutoOrient()); - if (options.FocusX.HasValue && options.FocusY.HasValue) + if (w > 0 || h > 0) { - resizeOptions.CenterCoordinates = new PointF( - +(options.FocusX.Value / 2f) + 0.5f, - -(options.FocusY.Value / 2f) + 0.5f - ); + var isCropUpsize = options.Mode == ResizeMode.CropUpsize; + + if (!Enum.TryParse(options.Mode.ToString(), true, out var resizeMode)) + { + resizeMode = ISResizeMode.Max; + } + + if (isCropUpsize) + { + resizeMode = ISResizeMode.Crop; + } + + if (w >= image.Width && h >= image.Height && resizeMode == ISResizeMode.Crop && !isCropUpsize) + { + resizeMode = ISResizeMode.BoxPad; + } + + var resizeOptions = new ISResizeOptions { Size = new Size(w, h), Mode = resizeMode }; + + if (options.FocusX.HasValue && options.FocusY.HasValue) + { + resizeOptions.CenterCoordinates = new PointF( + +(options.FocusX.Value / 2f) + 0.5f, + -(options.FocusY.Value / 2f) + 0.5f + ); + } + + image.Mutate(x => x.Resize(resizeOptions)); } - image.Mutate(x => x.Resize(resizeOptions)); + image.Save(destination, encoder); } - - image.Save(destination, encoder); } - - return Task.CompletedTask; + finally + { + semaphoreSlim.Release(); + } } public Task GetImageInfoAsync(Stream source) { + Guard.NotNull(source, nameof(source)); + ImageInfo? result = null; try @@ -108,22 +122,34 @@ namespace Squidex.Infrastructure.Assets.ImageSharp return Task.FromResult(result); } - public Task FixOrientationAsync(Stream source, Stream destination) + public async Task FixOrientationAsync(Stream source, Stream destination) { - using (var image = Image.Load(source, out var format)) - { - var encoder = Configuration.Default.ImageFormatsManager.FindEncoder(format); + Guard.NotNull(source, nameof(source)); + Guard.NotNull(destination, nameof(destination)); + + await semaphoreSlim.WaitAsync(); - if (encoder == null) + try + { + using (var image = Image.Load(source, out var format)) { - throw new NotSupportedException(); - } + var encoder = Configuration.Default.ImageFormatsManager.FindEncoder(format); + + if (encoder == null) + { + throw new NotSupportedException(); + } - image.Mutate(x => x.AutoOrient()); + image.Mutate(x => x.AutoOrient()); - image.Save(destination, encoder); + image.Save(destination, encoder); - return Task.FromResult(GetImageInfo(image)); + return GetImageInfo(image); + } + } + finally + { + semaphoreSlim.Release(); } }