using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.Extensions.Logging;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Web;
using SixLabors.ImageSharp.Web.Commands;
using SixLabors.ImageSharp.Web.Processors;
using ImageCropperCropCoordinates = Umbraco.Cms.Core.PropertyEditors.ValueConverters.ImageCropperValue.ImageCropperCropCoordinates;
namespace Umbraco.Cms.Web.Common.ImageProcessors
{
public class CropWebProcessor : IImageWebProcessor
{
///
/// The command constant for the crop definition
///
public const string Crop = "crop";
public FormattedImage Process(
FormattedImage image,
ILogger logger,
IDictionary commands,
CommandParser parser,
CultureInfo culture)
{
ImageCropperCropCoordinates cropCoordinates = GetCropCoordinates(commands, parser, culture);
if (cropCoordinates is null)
{
return image;
}
Size size = image.Image.Size();
Rectangle crop = GetCropRectangle(size.Width, size.Height, cropCoordinates);
image.Image.Mutate(x => x.Crop(crop));
return image;
}
private static readonly IEnumerable CropCommands = new[] {Crop};
public IEnumerable Commands { get; } = CropCommands;
///
/// Gets a rectangle that is calculated from crop coordinates.
///
/// The width of the image being cropped.
/// The height of the image being cropped.
/// Coordinate set to calculate rectangle from.
/// Rectangle with the position and sized described in coordinates.
private static Rectangle GetCropRectangle(int width, int height, ImageCropperCropCoordinates coordinates)
{
// Get coordinates of top left corner of the rectangle
var topX = RoundToInt(width * coordinates.X1);
var topY = RoundToInt(height * coordinates.Y1);
// Get coordinated of bottom right corner
var bottomX = RoundToInt(width - (width * coordinates.X2));
var bottomY = RoundToInt(height - (height * coordinates.Y2));
// Get width and height of crop
var cropWidth = bottomX - topX;
var cropHeight = bottomY - topY;
return new Rectangle(topX, topY, cropWidth, cropHeight);
}
///
/// Converts a decimal to an int with rounding.
///
/// Decimal to convert.
/// The decimal rounded to an int.
private static int RoundToInt(decimal number) => decimal.ToInt32(Math.Round(number));
///
/// Gets the crop coordinates from the query string.
///
/// Commands dictionary to parse the coordinates from.
/// Parser provided by imagesharp.
/// Culture to use for parsing.
/// Coordinates of the crop.
private static ImageCropperCropCoordinates GetCropCoordinates(IDictionary commands, CommandParser parser, CultureInfo culture)
{
decimal[] crops = parser.ParseValue(commands.GetValueOrDefault(Crop), culture);
if (crops.Length != 4)
{
return null;
}
return new ImageCropperCropCoordinates()
{
X1 = crops[0],
Y1 = crops[1],
X2 = crops[2],
Y2 = crops[3]
};
}
}
}