Browse Source

Optional parameter to change image quality.

pull/351/head
Sebastian Stehle 7 years ago
parent
commit
d516d72eff
  1. 2
      src/Squidex.Infrastructure/Assets/IAssetThumbnailGenerator.cs
  2. 65
      src/Squidex.Infrastructure/Assets/ImageSharp/ImageSharpAssetThumbnailGenerator.cs
  3. 11
      src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs
  4. 48
      tests/Squidex.Infrastructure.Tests/Assets/ImageSharpAssetThumbnailGeneratorTests.cs
  5. BIN
      tests/Squidex.Infrastructure.Tests/Assets/Images/logo.jpg
  6. BIN
      tests/Squidex.Infrastructure.Tests/Assets/Images/logo.png
  7. 8
      tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj

2
src/Squidex.Infrastructure/Assets/IAssetThumbnailGenerator.cs

@ -14,6 +14,6 @@ namespace Squidex.Infrastructure.Assets
{
Task<ImageInfo> GetImageInfoAsync(Stream source);
Task CreateThumbnailAsync(Stream source, Stream destination, int? width, int? height, string mode);
Task CreateThumbnailAsync(Stream source, Stream destination, int? width = null, int? height = null, string mode = null, int? quality = null);
}
}

65
src/Squidex.Infrastructure/Assets/ImageSharp/ImageSharpAssetThumbnailGenerator.cs

@ -9,6 +9,7 @@ using System;
using System.IO;
using System.Threading.Tasks;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Transforms;
using SixLabors.Primitives;
@ -17,49 +18,59 @@ namespace Squidex.Infrastructure.Assets.ImageSharp
{
public sealed class ImageSharpAssetThumbnailGenerator : IAssetThumbnailGenerator
{
public ImageSharpAssetThumbnailGenerator()
{
Configuration.Default.ImageFormatsManager.AddImageFormat(ImageFormats.Jpeg);
Configuration.Default.ImageFormatsManager.AddImageFormat(ImageFormats.Png);
}
public Task CreateThumbnailAsync(Stream source, Stream destination, int? width, int? height, string mode)
public Task CreateThumbnailAsync(Stream source, Stream destination, int? width = null, int? height = null, string mode = null, int? quality = null)
{
return Task.Run(() =>
{
if (width == null && height == null)
if (!width.HasValue && !height.HasValue && !quality.HasValue)
{
source.CopyTo(destination);
return;
}
var isCropUpsize = string.Equals("CropUpsize", mode, StringComparison.OrdinalIgnoreCase);
if (!Enum.TryParse<ResizeMode>(mode, true, out var resizeMode))
{
resizeMode = ResizeMode.Max;
}
if (isCropUpsize)
using (var sourceImage = Image.Load(source, out var format))
{
resizeMode = ResizeMode.Crop;
}
var encoder = Configuration.Default.ImageFormatsManager.FindEncoder(format);
var w = width ?? 0;
var h = height ?? 0;
if (quality.HasValue)
{
encoder = new JpegEncoder { Quality = quality.Value };
}
using (var sourceImage = Image.Load(source, out var format))
{
if (w >= sourceImage.Width && h >= sourceImage.Height && resizeMode == ResizeMode.Crop && !isCropUpsize)
if (encoder == null)
{
resizeMode = ResizeMode.BoxPad;
throw new NotSupportedException();
}
var options = new ResizeOptions { Size = new Size(w, h), Mode = resizeMode };
if (width.HasValue || height.HasValue)
{
var isCropUpsize = string.Equals("CropUpsize", mode, StringComparison.OrdinalIgnoreCase);
if (!Enum.TryParse<ResizeMode>(mode, true, out var resizeMode))
{
resizeMode = ResizeMode.Max;
}
if (isCropUpsize)
{
resizeMode = ResizeMode.Crop;
}
var resizeWidth = width ?? 0;
var resizeHeight = height ?? 0;
if (resizeWidth >= sourceImage.Width && resizeHeight >= sourceImage.Height && resizeMode == ResizeMode.Crop && !isCropUpsize)
{
resizeMode = ResizeMode.BoxPad;
}
var options = new ResizeOptions { Size = new Size(resizeWidth, resizeHeight), Mode = resizeMode };
sourceImage.Mutate(x => x.Resize(options));
}
sourceImage.Mutate(x => x.Resize(options));
sourceImage.Save(destination, format);
sourceImage.Save(destination, encoder);
}
});
}

11
src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs

@ -51,6 +51,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
/// <param name="version">The optional version of the asset.</param>
/// <param name="width">The target width of the asset, if it is an image.</param>
/// <param name="height">The target height of the asset, if it is an image.</param>
/// <param name="quality">Optional image quality, it is is an jpeg image.</param>
/// <param name="mode">The resize mode when the width and height is defined.</param>
/// <returns>
/// 200 => Asset found and content or (resized) image returned.
@ -64,11 +65,12 @@ namespace Squidex.Areas.Api.Controllers.Assets
[FromQuery] long version = EtagVersion.Any,
[FromQuery] int? width = null,
[FromQuery] int? height = null,
[FromQuery] int? quality = null,
[FromQuery] string mode = null)
{
var entity = await assetRepository.FindAssetAsync(id);
if (entity == null || entity.FileVersion < version || width == 0 || height == 0)
if (entity == null || entity.FileVersion < version || width == 0 || height == 0 || quality == 0)
{
return NotFound();
}
@ -83,6 +85,11 @@ namespace Squidex.Areas.Api.Controllers.Assets
{
var assetSuffix = $"{width}_{height}_{mode}";
if (quality.HasValue)
{
assetSuffix += $"_{quality}";
}
try
{
await assetStore.DownloadAsync(assetId, entity.FileVersion, assetSuffix, bodyStream);
@ -103,7 +110,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
using (Profiler.Trace("ResizeImage"))
{
await assetThumbnailGenerator.CreateThumbnailAsync(sourceStream, destinationStream, width, height, mode);
await assetThumbnailGenerator.CreateThumbnailAsync(sourceStream, destinationStream, width, height, mode, quality);
destinationStream.Position = 0;
}

48
tests/Squidex.Infrastructure.Tests/Assets/ImageSharpAssetThumbnailGeneratorTests.cs

@ -15,16 +15,15 @@ namespace Squidex.Infrastructure.Assets
{
public class ImageSharpAssetThumbnailGeneratorTests
{
private const string Image = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTM0A1t6AAAADElEQVQYV2P4//8/AAX+Av6nNYGEAAAAAElFTkSuQmCC";
private readonly ImageSharpAssetThumbnailGenerator sut = new ImageSharpAssetThumbnailGenerator();
private readonly MemoryStream target = new MemoryStream();
[Fact]
public async Task Should_return_same_image_if_no_size_is_passed_for_thumbnail()
{
var source = new MemoryStream(Convert.FromBase64String(Image));
var target = new MemoryStream();
var source = GetPng();
await sut.CreateThumbnailAsync(source, target, null, null, "resize");
await sut.CreateThumbnailAsync(source, target);
Assert.Equal(target.Length, source.Length);
}
@ -32,23 +31,42 @@ namespace Squidex.Infrastructure.Assets
[Fact]
public async Task Should_resize_image_to_target()
{
var source = new MemoryStream(Convert.FromBase64String(Image));
var target = new MemoryStream();
var source = GetPng();
await sut.CreateThumbnailAsync(source, target, 100, 100, "resize");
await sut.CreateThumbnailAsync(source, target, 1000, 1000, "resize");
Assert.True(target.Length > source.Length);
}
[Fact]
public async Task Should_change_jpeg_quality_and_write_to_target()
{
var source = GetJpeg();
await sut.CreateThumbnailAsync(source, target, quality: 10);
Assert.True(target.Length < source.Length);
}
[Fact]
public async Task Should_change_png_quality_and_write_to_target()
{
var source = GetPng();
await sut.CreateThumbnailAsync(source, target, quality: 10);
Assert.True(target.Length < source.Length);
}
[Fact]
public async Task Should_return_image_information_if_image_is_valid()
{
var source = new MemoryStream(Convert.FromBase64String(Image));
var source = GetPng();
var imageInfo = await sut.GetImageInfoAsync(source);
Assert.Equal(1, imageInfo.PixelHeight);
Assert.Equal(1, imageInfo.PixelWidth);
Assert.Equal(600, imageInfo.PixelHeight);
Assert.Equal(600, imageInfo.PixelWidth);
}
[Fact]
@ -60,5 +78,15 @@ namespace Squidex.Infrastructure.Assets
Assert.Null(imageInfo);
}
private Stream GetPng()
{
return GetType().Assembly.GetManifestResourceStream("Squidex.Infrastructure.Assets.Images.logo.png");
}
private Stream GetJpeg()
{
return GetType().Assembly.GetManifestResourceStream("Squidex.Infrastructure.Assets.Images.logo.jpg");
}
}
}

BIN
tests/Squidex.Infrastructure.Tests/Assets/Images/logo.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
tests/Squidex.Infrastructure.Tests/Assets/Images/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

8
tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj

@ -6,6 +6,10 @@
<RootNamespace>Squidex.Infrastructure</RootNamespace>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup>
<None Remove="Assets\Images\logo.jpg" />
<None Remove="Assets\Images\logo.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Squidex.Infrastructure.Azure\Squidex.Infrastructure.Azure.csproj" />
<ProjectReference Include="..\..\src\Squidex.Infrastructure.GoogleCloud\Squidex.Infrastructure.GoogleCloud.csproj" />
@ -34,4 +38,8 @@
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Assets\Images\logo.jpg" />
<EmbeddedResource Include="Assets\Images\logo.png" />
</ItemGroup>
</Project>
Loading…
Cancel
Save