Browse Source

Change format for assets. (#579)

pull/590/head
Sebastian Stehle 5 years ago
committed by GitHub
parent
commit
86745cd8d0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      backend/src/Squidex.Infrastructure/Assets/ImageFormat.cs
  2. 2
      backend/src/Squidex.Infrastructure/Assets/ImageInfo.cs
  3. 53
      backend/src/Squidex.Infrastructure/Assets/ImageSharp/ImageSharpAssetThumbnailGenerator.cs
  4. 10
      backend/src/Squidex.Infrastructure/Assets/ResizeOptions.cs
  5. 6
      backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetContentQueryDto.cs
  6. 61
      backend/tests/Squidex.Infrastructure.Tests/Assets/ImageSharpAssetThumbnailGeneratorTests.cs
  7. BIN
      backend/tests/Squidex.Infrastructure.Tests/Assets/Images/logo.gif
  8. 0
      backend/tests/Squidex.Infrastructure.Tests/Assets/Images/logo.jpeg
  9. BIN
      backend/tests/Squidex.Infrastructure.Tests/Assets/Images/logo.tga
  10. 8
      backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj

18
backend/src/Squidex.Infrastructure/Assets/ImageFormat.cs

@ -0,0 +1,18 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Infrastructure.Assets
{
public enum ImageFormat
{
Auto,
PNG,
JPEG,
TGA,
GIF
}
}

2
backend/src/Squidex.Infrastructure/Assets/ImageInfo.cs

@ -9,6 +9,8 @@ namespace Squidex.Infrastructure.Assets
{ {
public sealed class ImageInfo public sealed class ImageInfo
{ {
public string? Format { get; set; }
public int PixelWidth { get; } public int PixelWidth { get; }
public int PixelHeight { get; } public int PixelHeight { get; }

53
backend/src/Squidex.Infrastructure/Assets/ImageSharp/ImageSharpAssetThumbnailGenerator.cs

@ -10,7 +10,11 @@ using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp; using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using ISResizeMode = SixLabors.ImageSharp.Processing.ResizeMode; using ISResizeMode = SixLabors.ImageSharp.Processing.ResizeMode;
@ -44,17 +48,7 @@ namespace Squidex.Infrastructure.Assets.ImageSharp
{ {
using (var image = Image.Load(source, out var format)) using (var image = Image.Load(source, out var format))
{ {
var encoder = Configuration.Default.ImageFormatsManager.FindEncoder(format); var encoder = GetEncoder(options, format);
if (encoder == null)
{
throw new NotSupportedException();
}
if (options.Quality.HasValue && (encoder is JpegEncoder || !options.KeepFormat))
{
encoder = new JpegEncoder { Quality = options.Quality.Value };
}
image.Mutate(x => x.AutoOrient()); image.Mutate(x => x.AutoOrient());
@ -99,6 +93,39 @@ namespace Squidex.Infrastructure.Assets.ImageSharp
} }
} }
private static IImageEncoder GetEncoder(ResizeOptions options, SixLabors.ImageSharp.Formats.IImageFormat? format)
{
var encoder = Configuration.Default.ImageFormatsManager.FindEncoder(format);
if (encoder == null)
{
throw new NotSupportedException();
}
if (options.Quality.HasValue && (encoder is JpegEncoder || !options.KeepFormat) && options.Format == ImageFormat.Auto)
{
encoder = new JpegEncoder { Quality = options.Quality.Value };
}
else if (options.Format == ImageFormat.JPEG)
{
encoder = new JpegEncoder();
}
else if (options.Format == ImageFormat.PNG)
{
encoder = new PngEncoder();
}
else if (options.Format == ImageFormat.TGA)
{
encoder = new TgaEncoder();
}
else if (options.Format == ImageFormat.GIF)
{
encoder = new GifEncoder();
}
return encoder;
}
public Task<ImageInfo?> GetImageInfoAsync(Stream source) public Task<ImageInfo?> GetImageInfoAsync(Stream source)
{ {
Guard.NotNull(source, nameof(source)); Guard.NotNull(source, nameof(source));
@ -107,11 +134,13 @@ namespace Squidex.Infrastructure.Assets.ImageSharp
try try
{ {
var image = Image.Identify(source); var image = Image.Identify(source, out var format);
if (image != null) if (image != null)
{ {
result = GetImageInfo(image); result = GetImageInfo(image);
result.Format = format.Name;
} }
} }
catch catch

10
backend/src/Squidex.Infrastructure/Assets/ResizeOptions.cs

@ -11,6 +11,8 @@ namespace Squidex.Infrastructure.Assets
{ {
public sealed class ResizeOptions public sealed class ResizeOptions
{ {
public ImageFormat Format { get; set; }
public ResizeMode Mode { get; set; } public ResizeMode Mode { get; set; }
public int? Width { get; set; } public int? Width { get; set; }
@ -27,7 +29,7 @@ namespace Squidex.Infrastructure.Assets
public bool IsValid public bool IsValid
{ {
get { return Width > 0 || Height > 0 || Quality > 0; } get { return Width > 0 || Height > 0 || Quality > 0 || Format != ImageFormat.Auto; }
} }
public override string ToString() public override string ToString()
@ -58,6 +60,12 @@ namespace Squidex.Infrastructure.Assets
sb.Append(FocusY); sb.Append(FocusY);
} }
if (Format != ImageFormat.Auto)
{
sb.Append("_format_");
sb.Append(Format.ToString());
}
return sb.ToString(); return sb.ToString();
} }
} }

6
backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetContentQueryDto.cs

@ -87,6 +87,12 @@ namespace Squidex.Areas.Api.Controllers.Assets.Models
[FromQuery(Name = "force")] [FromQuery(Name = "force")]
public bool ForceResize { get; set; } public bool ForceResize { get; set; }
/// <summary>
/// True to force a new resize even if it already stored.
/// </summary>
[FromQuery(Name = "format")]
public ImageFormat Format { get; set; }
public ResizeOptions ToResizeOptions(IAssetEntity asset) public ResizeOptions ToResizeOptions(IAssetEntity asset)
{ {
Guard.NotNull(asset, nameof(asset)); Guard.NotNull(asset, nameof(asset));

61
backend/tests/Squidex.Infrastructure.Tests/Assets/ImageSharpAssetThumbnailGeneratorTests.cs

@ -6,7 +6,9 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Infrastructure.Assets.ImageSharp; using Squidex.Infrastructure.Assets.ImageSharp;
using Xunit; using Xunit;
@ -18,10 +20,43 @@ namespace Squidex.Infrastructure.Assets
private readonly ImageSharpAssetThumbnailGenerator sut = new ImageSharpAssetThumbnailGenerator(); private readonly ImageSharpAssetThumbnailGenerator sut = new ImageSharpAssetThumbnailGenerator();
private readonly MemoryStream target = new MemoryStream(); private readonly MemoryStream target = new MemoryStream();
public static IEnumerable<object[]> GetConversions()
{
var allFormats = Enum.GetValues(typeof(ImageFormat)).OfType<ImageFormat>().Where(x => x != ImageFormat.Auto);
foreach (var source in allFormats)
{
foreach (var target in allFormats)
{
if (!Equals(target, source))
{
yield return new object[] { target, source };
}
}
}
}
[Theory]
[MemberData(nameof(GetConversions))]
public async Task Should_convert_between_formats(ImageFormat sourceFormat, ImageFormat targetFormat)
{
var source = GetImage(sourceFormat);
var options = new ResizeOptions { Format = targetFormat };
await sut.CreateThumbnailAsync(source, target, options);
target.Position = 0;
var imageInfo = await sut.GetImageInfoAsync(target);
Assert.Equal(targetFormat.ToString(), imageInfo?.Format);
}
[Fact] [Fact]
public async Task Should_return_same_image_if_no_size_and_quality_is_passed_for_thumbnail() public async Task Should_return_same_image_if_no_size_and_quality_is_passed_for_thumbnail()
{ {
var source = GetPng(); var source = GetImage(ImageFormat.PNG);
await sut.CreateThumbnailAsync(source, target, new ResizeOptions()); await sut.CreateThumbnailAsync(source, target, new ResizeOptions());
@ -31,7 +66,7 @@ namespace Squidex.Infrastructure.Assets
[Fact] [Fact]
public async Task Should_resize_image_to_target() public async Task Should_resize_image_to_target()
{ {
var source = GetPng(); var source = GetImage(ImageFormat.PNG);
var options = new ResizeOptions { Width = 1000, Height = 1000, Mode = ResizeMode.BoxPad }; var options = new ResizeOptions { Width = 1000, Height = 1000, Mode = ResizeMode.BoxPad };
@ -43,7 +78,7 @@ namespace Squidex.Infrastructure.Assets
[Fact] [Fact]
public async Task Should_change_jpeg_quality_and_write_to_target() public async Task Should_change_jpeg_quality_and_write_to_target()
{ {
var source = GetJpeg(); var source = GetImage(ImageFormat.JPEG);
var options = new ResizeOptions { Quality = 10 }; var options = new ResizeOptions { Quality = 10 };
@ -55,7 +90,7 @@ namespace Squidex.Infrastructure.Assets
[Fact] [Fact]
public async Task Should_change_png_quality_and_write_to_target() public async Task Should_change_png_quality_and_write_to_target()
{ {
var source = GetPng(); var source = GetImage(ImageFormat.PNG);
var options = new ResizeOptions { Quality = 10 }; var options = new ResizeOptions { Quality = 10 };
@ -73,18 +108,20 @@ namespace Squidex.Infrastructure.Assets
Assert.Equal(135, imageInfo.PixelHeight); Assert.Equal(135, imageInfo.PixelHeight);
Assert.Equal(600, imageInfo.PixelWidth); Assert.Equal(600, imageInfo.PixelWidth);
Assert.False(imageInfo.IsRotatedOrSwapped); Assert.False(imageInfo.IsRotatedOrSwapped);
} }
[Fact] [Fact]
public async Task Should_return_image_information_if_image_is_valid() public async Task Should_return_image_information_if_image_is_valid()
{ {
var source = GetPng(); var source = GetImage(ImageFormat.PNG);
var imageInfo = await sut.GetImageInfoAsync(source); var imageInfo = await sut.GetImageInfoAsync(source);
Assert.Equal(600, imageInfo!.PixelHeight); Assert.Equal(600, imageInfo!.PixelHeight);
Assert.Equal(600, imageInfo!.PixelWidth); Assert.Equal(600, imageInfo!.PixelWidth);
Assert.False(imageInfo.IsRotatedOrSwapped); Assert.False(imageInfo.IsRotatedOrSwapped);
} }
@ -97,6 +134,7 @@ namespace Squidex.Infrastructure.Assets
Assert.Equal(600, imageInfo!.PixelHeight); Assert.Equal(600, imageInfo!.PixelHeight);
Assert.Equal(135, imageInfo!.PixelWidth); Assert.Equal(135, imageInfo!.PixelWidth);
Assert.True(imageInfo.IsRotatedOrSwapped); Assert.True(imageInfo.IsRotatedOrSwapped);
} }
@ -110,19 +148,18 @@ namespace Squidex.Infrastructure.Assets
Assert.Null(imageInfo); Assert.Null(imageInfo);
} }
private Stream GetPng() private Stream GetImage(ImageFormat format)
{ {
return GetType().Assembly.GetManifestResourceStream("Squidex.Infrastructure.Assets.Images.logo.png")!; var name = $"Squidex.Infrastructure.Assets.Images.logo.{format.ToString().ToLowerInvariant()}";
}
private Stream GetJpeg() return GetType().Assembly.GetManifestResourceStream(name)!;
{
return GetType().Assembly.GetManifestResourceStream("Squidex.Infrastructure.Assets.Images.logo.jpg")!;
} }
private Stream GetRotatedJpeg() private Stream GetRotatedJpeg()
{ {
return GetType().Assembly.GetManifestResourceStream("Squidex.Infrastructure.Assets.Images.logo-wide-rotated.jpg")!; var name = "Squidex.Infrastructure.Assets.Images.logo-wide-rotated.jpg";
return GetType().Assembly.GetManifestResourceStream(name)!;
} }
} }
} }

BIN
backend/tests/Squidex.Infrastructure.Tests/Assets/Images/logo.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

0
backend/tests/Squidex.Infrastructure.Tests/Assets/Images/logo.jpg → backend/tests/Squidex.Infrastructure.Tests/Assets/Images/logo.jpeg

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

BIN
backend/tests/Squidex.Infrastructure.Tests/Assets/Images/logo.tga

Binary file not shown.

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

@ -8,8 +8,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="Assets\Images\logo-wide-rotated.jpg" /> <None Remove="Assets\Images\logo-wide-rotated.jpg" />
<None Remove="Assets\Images\logo.jpg" /> <None Remove="Assets\Images\logo.gif" />
<None Remove="Assets\Images\logo.jpeg" />
<None Remove="Assets\Images\logo.png" /> <None Remove="Assets\Images\logo.png" />
<None Remove="Assets\Images\logo.tga" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Squidex.Infrastructure.Amazon\Squidex.Infrastructure.Amazon.csproj" /> <ProjectReference Include="..\..\src\Squidex.Infrastructure.Amazon\Squidex.Infrastructure.Amazon.csproj" />
@ -47,8 +49,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Assets\Images\logo-wide-rotated.jpg" /> <EmbeddedResource Include="Assets\Images\logo-wide-rotated.jpg" />
<EmbeddedResource Include="Assets\Images\logo.jpg" /> <EmbeddedResource Include="Assets\Images\logo.gif" />
<EmbeddedResource Include="Assets\Images\logo.jpeg" />
<EmbeddedResource Include="Assets\Images\logo.png" /> <EmbeddedResource Include="Assets\Images\logo.png" />
<EmbeddedResource Include="Assets\Images\logo.tga" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Translations\SampleResources.Designer.cs"> <Compile Update="Translations\SampleResources.Designer.cs">

Loading…
Cancel
Save