v10: Update to ImageSharp v2 (#12185)
* Update to ImageSharp 2.1.0 and ImageSharp.Web 2.0.0-alpha.0.23 * Rename CachedNameLength to CacheHashLength and add CacheFolderDepth setting * Replace PhysicalFileSystemProvider with WebRootImageProvider * Support EXIF-orientation in image dimention extractor * Remove virtual methods on FileProviderImageProvider * Simplify FileInfoImageResolver * Update to SixLabors.ImageSharp.Web 2.0.0-alpha.0.25 and remove custom providers * Make CropWebProcessor EXIF orientation-aware * Improve width/height sanitization * Also use 'v' as cache buster value * Add WebP to supported image file types * Update to SixLabors.ImageSharp.Web 2.0.0-alpha.0.27 and fix test * Fix rounding error and add test cases * Update to newest and stable releases * Move ImageSharpImageUrlGenerator to Umbraco.Web.Common * Use IConfigureOptions to configure ImageSharp options * Implement IEquatable on ImageUrlGenerationOptions classes * Fix empty/null values in image URL generation and corresponding tests * Use IsSupportedImageFormat extension method * Remove unneeded reflection
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SixLabors.ImageSharp.Web;
|
||||
using SixLabors.ImageSharp.Web.Commands;
|
||||
@@ -20,45 +22,71 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors
|
||||
/// </summary>
|
||||
public const string Coordinates = "cc";
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <summary>
|
||||
/// The command constant for the resize orientation handling mode.
|
||||
/// </summary>
|
||||
public const string Orient = "orient";
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<string> Commands { get; } = new[]
|
||||
{
|
||||
Coordinates
|
||||
Coordinates,
|
||||
Orient
|
||||
};
|
||||
|
||||
/// <inheritdoc/>
|
||||
public FormattedImage Process(FormattedImage image, ILogger logger, IDictionary<string, string> commands, CommandParser parser, CultureInfo culture)
|
||||
/// <inheritdoc />
|
||||
public FormattedImage Process(FormattedImage image, ILogger logger, CommandCollection commands, CommandParser parser, CultureInfo culture)
|
||||
{
|
||||
RectangleF? coordinates = GetCoordinates(commands, parser, culture);
|
||||
if (coordinates != null)
|
||||
Rectangle? cropRectangle = GetCropRectangle(image, commands, parser, culture);
|
||||
if (cropRectangle.HasValue)
|
||||
{
|
||||
// Convert the coordinates to a pixel based rectangle
|
||||
int sourceWidth = image.Image.Width;
|
||||
int sourceHeight = image.Image.Height;
|
||||
int x = (int)MathF.Round(coordinates.Value.X * sourceWidth);
|
||||
int y = (int)MathF.Round(coordinates.Value.Y * sourceHeight);
|
||||
int width = (int)MathF.Round(coordinates.Value.Width * sourceWidth);
|
||||
int height = (int)MathF.Round(coordinates.Value.Height * sourceHeight);
|
||||
|
||||
var cropRectangle = new Rectangle(x, y, width, height);
|
||||
|
||||
image.Image.Mutate(x => x.Crop(cropRectangle));
|
||||
image.Image.Mutate(x => x.Crop(cropRectangle.Value));
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private static RectangleF? GetCoordinates(IDictionary<string, string> commands, CommandParser parser, CultureInfo culture)
|
||||
/// <inheritdoc />
|
||||
public bool RequiresTrueColorPixelFormat(CommandCollection commands, CommandParser parser, CultureInfo culture) => false;
|
||||
|
||||
private static Rectangle? GetCropRectangle(FormattedImage image, CommandCollection commands, CommandParser parser, CultureInfo culture)
|
||||
{
|
||||
float[] coordinates = parser.ParseValue<float[]>(commands.GetValueOrDefault(Coordinates), culture);
|
||||
|
||||
if (coordinates.Length != 4)
|
||||
if (coordinates.Length != 4 ||
|
||||
(coordinates[0] == 0 && coordinates[1] == 0 && coordinates[2] == 0 && coordinates[3] == 0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// The right and bottom values are actually the distance from those sides, so convert them into real coordinates
|
||||
return RectangleF.FromLTRB(coordinates[0], coordinates[1], 1 - coordinates[2], 1 - coordinates[3]);
|
||||
// The right and bottom values are actually the distance from those sides, so convert them into real coordinates and transform to correct orientation
|
||||
float left = Math.Clamp(coordinates[0], 0, 1);
|
||||
float top = Math.Clamp(coordinates[1], 0, 1);
|
||||
float right = Math.Clamp(1 - coordinates[2], 0, 1);
|
||||
float bottom = Math.Clamp(1 - coordinates[3], 0, 1);
|
||||
ushort orientation = GetExifOrientation(image, commands, parser, culture);
|
||||
Vector2 xy1 = ExifOrientationUtilities.Transform(new Vector2(left, top), Vector2.Zero, Vector2.One, orientation);
|
||||
Vector2 xy2 = ExifOrientationUtilities.Transform(new Vector2(right, bottom), Vector2.Zero, Vector2.One, orientation);
|
||||
|
||||
// Scale points to a pixel based rectangle
|
||||
Size size = image.Image.Size();
|
||||
|
||||
return Rectangle.Round(RectangleF.FromLTRB(
|
||||
MathF.Min(xy1.X, xy2.X) * size.Width,
|
||||
MathF.Min(xy1.Y, xy2.Y) * size.Height,
|
||||
MathF.Max(xy1.X, xy2.X) * size.Width,
|
||||
MathF.Max(xy1.Y, xy2.Y) * size.Height));
|
||||
}
|
||||
|
||||
private static ushort GetExifOrientation(FormattedImage image, CommandCollection commands, CommandParser parser, CultureInfo culture)
|
||||
{
|
||||
if (commands.Contains(Orient) && !parser.ParseValue<bool>(commands.GetValueOrDefault(Orient), culture))
|
||||
{
|
||||
return ExifOrientationMode.Unknown;
|
||||
}
|
||||
|
||||
image.TryGetExifOrientation(out ushort orientation);
|
||||
|
||||
return orientation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user