diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs
index 3fe4db8344..d5e2adb2e5 100644
--- a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs
+++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs
@@ -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
{
+ ///
+ /// Allows the cropping of images.
+ ///
public class CropWebProcessor : IImageWebProcessor
{
///
- /// The command constant for the crop definition
+ /// 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)
+ ///
+ /// The command constant for the crop mode.
+ ///
+ public const string Mode = "cropmode";
+
+
+ ///
+ public IEnumerable Commands { get; } = new[]
{
- ImageCropperCropCoordinates cropCoordinates = GetCropCoordinates(commands, parser, culture);
- if (cropCoordinates is null)
+ Crop,
+ Mode
+ };
+
+ ///
+ public FormattedImage Process(FormattedImage image, ILogger logger, IDictionary 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 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)
+ private static RectangleF? GetCrop(IDictionary 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(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 commands, CommandParser parser, CultureInfo culture)
+ => parser.ParseValue(commands.GetValueOrDefault(Mode), culture);
+ }
+
+ ///
+ /// Enumerated cop modes to apply to cropped images.
+ ///
+ public enum CropMode
+ {
///
- /// Converts a decimal to an int with rounding.
+ /// Crops the image using the standard rectangle model of x, y, width, height.
///
- /// Decimal to convert.
- /// The decimal rounded to an int.
- private static int RoundToInt(decimal number) => decimal.ToInt32(Math.Round(number));
-
+ Pixels,
///
- /// Gets the crop coordinates from the query string.
+ /// Crops the image using percentages model left, top, right, bottom.
///
- /// 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]
- };
- }
+ Percentage
}
}