Add cropmode support and align logic to ImageProcessors Crop processor
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -7,93 +6,101 @@ 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows the cropping of images.
|
||||
/// </summary>
|
||||
public class CropWebProcessor : IImageWebProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// The command constant for the crop definition
|
||||
/// The command constant for the crop definition.
|
||||
/// </summary>
|
||||
public const string Crop = "crop";
|
||||
|
||||
public FormattedImage Process(
|
||||
FormattedImage image,
|
||||
ILogger logger,
|
||||
IDictionary<string, string> commands,
|
||||
CommandParser parser,
|
||||
CultureInfo culture)
|
||||
/// <summary>
|
||||
/// The command constant for the crop mode.
|
||||
/// </summary>
|
||||
public const string Mode = "cropmode";
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<string> Commands { get; } = new[]
|
||||
{
|
||||
ImageCropperCropCoordinates cropCoordinates = GetCropCoordinates(commands, parser, culture);
|
||||
if (cropCoordinates is null)
|
||||
Crop,
|
||||
Mode
|
||||
};
|
||||
|
||||
/// <inheritdoc/>
|
||||
public FormattedImage Process(FormattedImage image, ILogger logger, IDictionary<string, string> commands, CommandParser parser, CultureInfo culture)
|
||||
{
|
||||
if (GetCrop(commands, parser, culture) is RectangleF crop)
|
||||
{
|
||||
return image;
|
||||
Size size = image.Image.Size();
|
||||
|
||||
// Convert from percentages to pixels based on crop mode
|
||||
if (GetMode(commands, parser, culture) is CropMode.Percentage)
|
||||
{
|
||||
// Fix for whole numbers
|
||||
float percentageLeft = crop.Left > 1 ? crop.Left / 100 : crop.Left;
|
||||
float percentageRight = crop.Right > 1 ? crop.Right / 100 : crop.Right;
|
||||
float percentageTop = crop.Top > 1 ? crop.Top / 100 : crop.Top;
|
||||
float percentageBottom = crop.Bottom > 1 ? crop.Bottom / 100 : crop.Bottom;
|
||||
|
||||
// Work out the percentages
|
||||
float left = percentageLeft * size.Width;
|
||||
float top = percentageTop * size.Height;
|
||||
float width = percentageRight < 1 ? (1 - percentageLeft - percentageRight) * size.Width : size.Width;
|
||||
float height = percentageBottom < 1 ? (1 - percentageTop - percentageBottom) * size.Height : size.Height;
|
||||
|
||||
crop = new RectangleF(left, top, width, height);
|
||||
}
|
||||
|
||||
// Round and validate crop rectangle
|
||||
var rectangle = Rectangle.Round(crop);
|
||||
if (rectangle.X < size.Width && rectangle.Y < size.Height)
|
||||
{
|
||||
if (rectangle.Width > (size.Width - rectangle.X))
|
||||
{
|
||||
rectangle.Width = size.Width - rectangle.X;
|
||||
}
|
||||
|
||||
if (rectangle.Height > (size.Height - rectangle.Y))
|
||||
{
|
||||
rectangle.Height = size.Height - rectangle.Y;
|
||||
}
|
||||
|
||||
image.Image.Mutate(x => x.Crop(rectangle));
|
||||
}
|
||||
}
|
||||
|
||||
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<string> CropCommands = new[] {Crop};
|
||||
|
||||
public IEnumerable<string> Commands { get; } = CropCommands;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a rectangle that is calculated from crop coordinates.
|
||||
/// </summary>
|
||||
/// <param name="width">The width of the image being cropped.</param>
|
||||
/// <param name="height">The height of the image being cropped.</param>
|
||||
/// <param name="coordinates">Coordinate set to calculate rectangle from.</param>
|
||||
/// <returns>Rectangle with the position and sized described in coordinates.</returns>
|
||||
private static Rectangle GetCropRectangle(int width, int height, ImageCropperCropCoordinates coordinates)
|
||||
private static RectangleF? GetCrop(IDictionary<string, string> commands, CommandParser parser, CultureInfo culture)
|
||||
{
|
||||
// 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));
|
||||
float[] crops = parser.ParseValue<float[]>(commands.GetValueOrDefault(Crop), culture);
|
||||
|
||||
// Get width and height of crop
|
||||
var cropWidth = bottomX - topX;
|
||||
var cropHeight = bottomY - topY;
|
||||
|
||||
return new Rectangle(topX, topY, cropWidth, cropHeight);
|
||||
return (crops.Length != 4) ? null : new RectangleF(crops[0], crops[1], crops[2], crops[3]);
|
||||
}
|
||||
|
||||
private static CropMode GetMode(IDictionary<string, string> commands, CommandParser parser, CultureInfo culture)
|
||||
=> parser.ParseValue<CropMode>(commands.GetValueOrDefault(Mode), culture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerated cop modes to apply to cropped images.
|
||||
/// </summary>
|
||||
public enum CropMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a decimal to an int with rounding.
|
||||
/// Crops the image using the standard rectangle model of x, y, width, height.
|
||||
/// </summary>
|
||||
/// <param name="number">Decimal to convert.</param>
|
||||
/// <returns>The decimal rounded to an int.</returns>
|
||||
private static int RoundToInt(decimal number) => decimal.ToInt32(Math.Round(number));
|
||||
|
||||
Pixels,
|
||||
/// <summary>
|
||||
/// Gets the crop coordinates from the query string.
|
||||
/// Crops the image using percentages model left, top, right, bottom.
|
||||
/// </summary>
|
||||
/// <param name="commands">Commands dictionary to parse the coordinates from.</param>
|
||||
/// <param name="parser">Parser provided by imagesharp.</param>
|
||||
/// <param name="culture">Culture to use for parsing.</param>
|
||||
/// <returns>Coordinates of the crop.</returns>
|
||||
private static ImageCropperCropCoordinates GetCropCoordinates(IDictionary<string, string> commands, CommandParser parser, CultureInfo culture)
|
||||
{
|
||||
decimal[] crops = parser.ParseValue<decimal[]>(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]
|
||||
};
|
||||
}
|
||||
Percentage
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user