diff --git a/src/Umbraco.Core/Media/IImageUrlGenerator.cs b/src/Umbraco.Core/Media/IImageUrlGenerator.cs
index c8628f3147b0..bf6112756111 100644
--- a/src/Umbraco.Core/Media/IImageUrlGenerator.cs
+++ b/src/Umbraco.Core/Media/IImageUrlGenerator.cs
@@ -1,12 +1,28 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using Umbraco.Cms.Core.Models;
namespace Umbraco.Cms.Core.Media
{
+ ///
+ /// Exposes a method that generates an image URL based on the specified options.
+ ///
public interface IImageUrlGenerator
{
+ ///
+ /// Gets the supported image file types/extensions.
+ ///
+ ///
+ /// The supported image file types/extensions.
+ ///
IEnumerable SupportedImageFileTypes { get; }
+ ///
+ /// Gets the image URL based on the specified .
+ ///
+ /// The image URL generation options.
+ ///
+ /// The generated image URL.
+ ///
string GetImageUrl(ImageUrlGenerationOptions options);
}
}
diff --git a/src/Umbraco.Core/Models/ImageCropRatioMode.cs b/src/Umbraco.Core/Models/ImageCropRatioMode.cs
deleted file mode 100644
index 19f69cbeac1d..000000000000
--- a/src/Umbraco.Core/Models/ImageCropRatioMode.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Umbraco.Cms.Core.Models
-{
- public enum ImageCropRatioMode
- {
- Width,
- Height
- }
-}
diff --git a/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs b/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs
index 99f42bbefa5f..ba5c1d0e307f 100644
--- a/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs
+++ b/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs
@@ -1,66 +1,68 @@
-namespace Umbraco.Cms.Core.Models
+namespace Umbraco.Cms.Core.Models
{
///
- /// These are options that are passed to the IImageUrlGenerator implementation to determine
- /// the propery URL that is needed
+ /// These are options that are passed to the IImageUrlGenerator implementation to determine the URL that is generated.
///
public class ImageUrlGenerationOptions
{
- public ImageUrlGenerationOptions (string imageUrl)
- {
- ImageUrl = imageUrl;
- }
+ public ImageUrlGenerationOptions(string imageUrl) => ImageUrl = imageUrl;
public string ImageUrl { get; }
+
public int? Width { get; set; }
+
public int? Height { get; set; }
- public decimal? WidthRatio { get; set; }
- public decimal? HeightRatio { get; set; }
+
public int? Quality { get; set; }
+
public ImageCropMode? ImageCropMode { get; set; }
+
public ImageCropAnchor? ImageCropAnchor { get; set; }
- public bool DefaultCrop { get; set; }
+
public FocalPointPosition FocalPoint { get; set; }
+
public CropCoordinates Crop { get; set; }
+
public string CacheBusterValue { get; set; }
+
public string FurtherOptions { get; set; }
- public bool UpScale { get; set; } = true;
- public string AnimationProcessMode { get; set; }
///
- /// The focal point position, in whatever units the registered IImageUrlGenerator uses,
- /// typically a percentage of the total image from 0.0 to 1.0.
+ /// The focal point position, in whatever units the registered IImageUrlGenerator uses, typically a percentage of the total image from 0.0 to 1.0.
///
public class FocalPointPosition
{
- public FocalPointPosition (decimal top, decimal left)
+ public FocalPointPosition(decimal left, decimal top)
{
Left = left;
Top = top;
}
public decimal Left { get; }
+
public decimal Top { get; }
}
///
- /// The bounds of the crop within the original image, in whatever units the registered
- /// IImageUrlGenerator uses, typically a percentage between 0 and 100.
+ /// The bounds of the crop within the original image, in whatever units the registered IImageUrlGenerator uses, typically a percentage between 0.0 and 1.0.
///
public class CropCoordinates
{
- public CropCoordinates (decimal x1, decimal y1, decimal x2, decimal y2)
+ public CropCoordinates(decimal left, decimal top, decimal right, decimal bottom)
{
- X1 = x1;
- Y1 = y1;
- X2 = x2;
- Y2 = y2;
+ Left = left;
+ Top = top;
+ Right = right;
+ Bottom = bottom;
}
- public decimal X1 { get; }
- public decimal Y1 { get; }
- public decimal X2 { get; }
- public decimal Y2 { get; }
+ public decimal Left { get; }
+
+ public decimal Top { get; }
+
+ public decimal Right { get; }
+
+ public decimal Bottom { get; }
}
}
}
diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
index 5b7e4a336c6c..8388841e179e 100644
--- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
+++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
@@ -144,8 +144,6 @@ public static IUmbracoBuilder AddCoreInitialServices(this IUmbracoBuilder builde
builder.PropertyValueConverters()
.Remove();
- builder.Services.AddUnique();
-
// register *all* checks, except those marked [HideFromTypeFinder] of course
builder.Services.AddUnique();
@@ -180,7 +178,10 @@ public static IUmbracoBuilder AddCoreInitialServices(this IUmbracoBuilder builde
builder.Services.AddUnique();
+ // Add default ImageSharp configuration and service implementations
+ builder.Services.AddUnique(SixLabors.ImageSharp.Configuration.Default);
builder.Services.AddUnique();
+ builder.Services.AddUnique();
builder.Services.AddUnique();
diff --git a/src/Umbraco.Infrastructure/Media/ImageDimensionExtractor.cs b/src/Umbraco.Infrastructure/Media/ImageDimensionExtractor.cs
index f8dc089a0d73..380704c26c3e 100644
--- a/src/Umbraco.Infrastructure/Media/ImageDimensionExtractor.cs
+++ b/src/Umbraco.Infrastructure/Media/ImageDimensionExtractor.cs
@@ -8,6 +8,17 @@ namespace Umbraco.Cms.Infrastructure.Media
{
internal class ImageDimensionExtractor : IImageDimensionExtractor
{
+ ///
+ /// The ImageSharp configuration.
+ ///
+ private readonly Configuration _configuration;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The ImageSharp configuration.
+ public ImageDimensionExtractor(Configuration configuration) => _configuration = configuration;
+
///
/// Gets the dimensions of an image.
///
@@ -39,7 +50,7 @@ public ImageSize GetDimensions(Stream stream)
stream.Seek(0, SeekOrigin.Begin);
}
- using (var image = Image.Load(stream))
+ using (var image = Image.Load(_configuration, stream))
{
var fileWidth = image.Width;
var fileHeight = image.Height;
diff --git a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs
index e1bf2c197b2d..ecde6790d2d7 100644
--- a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs
+++ b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs
@@ -1,68 +1,107 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Globalization;
+using System.Linq;
using System.Text;
+using SixLabors.ImageSharp;
using Umbraco.Cms.Core.Media;
using Umbraco.Cms.Core.Models;
-using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Media
{
+ ///
+ /// Exposes a method that generates an image URL based on the specified options that can be processed by ImageSharp.
+ ///
+ ///
public class ImageSharpImageUrlGenerator : IImageUrlGenerator
{
- public IEnumerable SupportedImageFileTypes => new[] { "jpeg", "jpg", "gif", "bmp", "png" };
+ ///
+ public IEnumerable SupportedImageFileTypes { get; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The ImageSharp configuration.
+ public ImageSharpImageUrlGenerator(Configuration configuration)
+ : this(configuration.ImageFormats.SelectMany(f => f.FileExtensions).ToArray())
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The supported image file types/extensions.
+ ///
+ /// This constructor is only used for testing.
+ ///
+ internal ImageSharpImageUrlGenerator(IEnumerable supportedImageFileTypes) => SupportedImageFileTypes = supportedImageFileTypes;
+
+ ///
public string GetImageUrl(ImageUrlGenerationOptions options)
{
- if (options == null) return null;
+ if (options == null)
+ {
+ return null;
+ }
- var imageProcessorUrl = new StringBuilder(options.ImageUrl ?? string.Empty);
+ var imageUrl = new StringBuilder(options.ImageUrl);
- if (options.FocalPoint != null) AppendFocalPoint(imageProcessorUrl, options);
- else if (options.Crop != null) AppendCrop(imageProcessorUrl, options);
- else if (options.DefaultCrop) imageProcessorUrl.Append("?anchor=center&mode=crop");
- else
+ bool queryStringHasStarted = false;
+ void AppendQueryString(string value)
{
- imageProcessorUrl.Append("?mode=").Append((options.ImageCropMode ?? ImageCropMode.Crop).ToString().ToLower());
+ imageUrl.Append(queryStringHasStarted ? '&' : '?');
+ queryStringHasStarted = true;
- if (options.ImageCropAnchor != null) imageProcessorUrl.Append("&anchor=").Append(options.ImageCropAnchor.ToString().ToLower());
+ imageUrl.Append(value);
}
+ void AddQueryString(string key, params IConvertible[] values)
+ => AppendQueryString(key + '=' + string.Join(",", values.Select(x => x.ToString(CultureInfo.InvariantCulture))));
- var hasFormat = options.FurtherOptions != null && options.FurtherOptions.InvariantContains("&format=");
+ if (options.FocalPoint != null)
+ {
+ AddQueryString("rxy", options.FocalPoint.Left, options.FocalPoint.Top);
+ }
- //Only put quality here, if we don't have a format specified.
- //Otherwise we need to put quality at the end to avoid it being overridden by the format.
- if (options.Quality.HasValue && hasFormat == false) imageProcessorUrl.Append("&quality=").Append(options.Quality);
- if (options.HeightRatio.HasValue) imageProcessorUrl.Append("&heightratio=").Append(options.HeightRatio.Value.ToString(CultureInfo.InvariantCulture));
- if (options.WidthRatio.HasValue) imageProcessorUrl.Append("&widthratio=").Append(options.WidthRatio.Value.ToString(CultureInfo.InvariantCulture));
- if (options.Width.HasValue) imageProcessorUrl.Append("&width=").Append(options.Width);
- if (options.Height.HasValue) imageProcessorUrl.Append("&height=").Append(options.Height);
- if (options.UpScale == false) imageProcessorUrl.Append("&upscale=false");
- if (!string.IsNullOrWhiteSpace(options.AnimationProcessMode)) imageProcessorUrl.Append("&animationprocessmode=").Append(options.AnimationProcessMode);
- if (!string.IsNullOrWhiteSpace(options.FurtherOptions)) imageProcessorUrl.Append(options.FurtherOptions);
+ if (options.Crop != null)
+ {
+ AddQueryString("cc", options.Crop.Left, options.Crop.Top, options.Crop.Right, options.Crop.Bottom);
+ }
- //If furtherOptions contains a format, we need to put the quality after the format.
- if (options.Quality.HasValue && hasFormat) imageProcessorUrl.Append("&quality=").Append(options.Quality);
- if (!string.IsNullOrWhiteSpace(options.CacheBusterValue)) imageProcessorUrl.Append("&rnd=").Append(options.CacheBusterValue);
+ if (options.ImageCropMode.HasValue)
+ {
+ AddQueryString("rmode", options.ImageCropMode.Value.ToString().ToLowerInvariant());
+ }
- return imageProcessorUrl.ToString();
- }
+ if (options.ImageCropAnchor.HasValue)
+ {
+ AddQueryString("ranchor", options.ImageCropAnchor.Value.ToString().ToLowerInvariant());
+ }
- private void AppendFocalPoint(StringBuilder imageProcessorUrl, ImageUrlGenerationOptions options)
- {
- imageProcessorUrl.Append("?center=");
- imageProcessorUrl.Append(options.FocalPoint.Top.ToString(CultureInfo.InvariantCulture)).Append(",");
- imageProcessorUrl.Append(options.FocalPoint.Left.ToString(CultureInfo.InvariantCulture));
- imageProcessorUrl.Append("&mode=crop");
- }
+ if (options.Width.HasValue)
+ {
+ AddQueryString("width", options.Width.Value);
+ }
- private void AppendCrop(StringBuilder imageProcessorUrl, ImageUrlGenerationOptions options)
- {
- imageProcessorUrl.Append("?crop=");
- imageProcessorUrl.Append(options.Crop.X1.ToString(CultureInfo.InvariantCulture)).Append(",");
- imageProcessorUrl.Append(options.Crop.Y1.ToString(CultureInfo.InvariantCulture)).Append(",");
- imageProcessorUrl.Append(options.Crop.X2.ToString(CultureInfo.InvariantCulture)).Append(",");
- imageProcessorUrl.Append(options.Crop.Y2.ToString(CultureInfo.InvariantCulture));
- imageProcessorUrl.Append("&cropmode=percentage");
+ if (options.Height.HasValue)
+ {
+ AddQueryString("height", options.Height.Value);
+ }
+
+ if (options.Quality.HasValue)
+ {
+ AddQueryString("quality", options.Quality.Value);
+ }
+
+ if (string.IsNullOrWhiteSpace(options.FurtherOptions) == false)
+ {
+ AppendQueryString(options.FurtherOptions.TrimStart('?', '&'));
+ }
+
+ if (string.IsNullOrWhiteSpace(options.CacheBusterValue) == false)
+ {
+ AddQueryString("rnd", options.CacheBusterValue);
+ }
+
+ return imageUrl.ToString();
}
}
}
diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs
index 32101d6cf766..c0efaac4ae57 100644
--- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs
+++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Umbraco.
+// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
@@ -63,13 +63,11 @@ public ImageCropperCrop GetCrop(string alias)
: Crops.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}
- public ImageUrlGenerationOptions GetCropBaseOptions(string url, ImageCropperCrop crop, bool defaultCrop, bool preferFocalPoint)
+ public ImageUrlGenerationOptions GetCropBaseOptions(string url, ImageCropperCrop crop, bool preferFocalPoint)
{
- if (preferFocalPoint && HasFocalPoint()
- || crop != null && crop.Coordinates == null && HasFocalPoint()
- || defaultCrop && HasFocalPoint())
+ if ((preferFocalPoint && HasFocalPoint()) || (crop != null && crop.Coordinates == null && HasFocalPoint()))
{
- return new ImageUrlGenerationOptions(url) { FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(FocalPoint.Top, FocalPoint.Left) };
+ return new ImageUrlGenerationOptions(url) { FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(FocalPoint.Left, FocalPoint.Top) };
}
else if (crop != null && crop.Coordinates != null && preferFocalPoint == false)
{
@@ -77,7 +75,7 @@ public ImageUrlGenerationOptions GetCropBaseOptions(string url, ImageCropperCrop
}
else
{
- return new ImageUrlGenerationOptions(url) { DefaultCrop = true };
+ return new ImageUrlGenerationOptions(url);
}
}
@@ -92,7 +90,7 @@ public string GetCropUrl(string alias, IImageUrlGenerator imageUrlGenerator, boo
if (crop == null && !string.IsNullOrWhiteSpace(alias))
return null;
- var options = GetCropBaseOptions(string.Empty, crop, string.IsNullOrWhiteSpace(alias), useFocalPoint);
+ var options = GetCropBaseOptions(null, crop, useFocalPoint || string.IsNullOrWhiteSpace(alias));
if (crop != null && useCropDimensions)
{
@@ -108,9 +106,9 @@ public string GetCropUrl(string alias, IImageUrlGenerator imageUrlGenerator, boo
///
/// Gets the value image URL for a specific width and height.
///
- public string GetCropUrl(int width, int height, IImageUrlGenerator imageUrlGenerator, bool useFocalPoint = false, string cacheBusterValue = null)
+ public string GetCropUrl(int width, int height, IImageUrlGenerator imageUrlGenerator, string cacheBusterValue = null)
{
- var options = GetCropBaseOptions(string.Empty, null, true, useFocalPoint);
+ var options = GetCropBaseOptions(null, null, false);
options.Width = width;
options.Height = height;
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs
index 52ee1ed0e4d1..a531aa6bbd01 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs
@@ -12,40 +12,40 @@ public class ImageSharpImageUrlGeneratorTests
{
private const string MediaPath = "/media/1005/img_0671.jpg";
private static readonly ImageUrlGenerationOptions.CropCoordinates s_crop = new ImageUrlGenerationOptions.CropCoordinates(0.58729977382575338m, 0.055768992440203169m, 0m, 0.32457553600198386m);
- private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus1 = new ImageUrlGenerationOptions.FocalPointPosition(0.80827067669172936m, 0.96m);
- private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus2 = new ImageUrlGenerationOptions.FocalPointPosition(0.41m, 0.4275m);
- private static readonly ImageSharpImageUrlGenerator s_generator = new ImageSharpImageUrlGenerator();
+ private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus1 = new ImageUrlGenerationOptions.FocalPointPosition(0.96m, 0.80827067669172936m);
+ private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus2 = new ImageUrlGenerationOptions.FocalPointPosition(0.4275m, 0.41m);
+ private static readonly ImageSharpImageUrlGenerator s_generator = new ImageSharpImageUrlGenerator(new string[0]);
[Test]
public void GetCropUrl_CropAliasTest()
{
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = s_crop, Width = 100, Height = 100 });
- Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString);
+ Assert.AreEqual(MediaPath + "?cc=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100", urlString);
}
[Test]
public void GetCropUrl_WidthHeightTest()
{
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 200, Height = 300 });
- Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300", urlString);
+ Assert.AreEqual(MediaPath + "?rxy=0.96,0.80827067669172936&width=200&height=300", urlString);
}
[Test]
public void GetCropUrl_FocalPointTest()
{
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 100, Height = 100 });
- Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=100&height=100", urlString);
+ Assert.AreEqual(MediaPath + "?rxy=0.96,0.80827067669172936&width=100&height=100", urlString);
}
[Test]
public void GetCropUrlFurtherOptionsTest()
{
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 200, Height = 300, FurtherOptions = "&filter=comic&roundedcorners=radius-26|bgcolor-fff" });
- Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString);
+ Assert.AreEqual(MediaPath + "?rxy=0.96,0.80827067669172936&width=200&height=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString);
}
///
- /// Test that if a crop alias has been specified that doesn't exist the method returns null
+ /// Test that if options is null, the generated image URL is also null.
///
[Test]
public void GetCropUrlNullTest()
@@ -55,13 +55,13 @@ public void GetCropUrlNullTest()
}
///
- /// Test that if a crop alias has been specified that doesn't exist the method returns null
+ /// Test that if the image URL is null, the generated image URL is empty.
///
[Test]
public void GetCropUrlEmptyTest()
{
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(null));
- Assert.AreEqual("?mode=crop", urlString);
+ Assert.AreEqual(string.Empty, urlString);
}
///
@@ -71,37 +71,7 @@ public void GetCropUrlEmptyTest()
public void GetBaseCropUrlFromModelTest()
{
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(null) { Crop = s_crop, Width = 100, Height = 100 });
- Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString);
- }
-
- ///
- /// Test the height ratio mode with predefined crop dimensions
- ///
- [Test]
- public void GetCropUrl_CropAliasHeightRatioModeTest()
- {
- var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = s_crop, Width = 100, HeightRatio = 1 });
- Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&heightratio=1&width=100", urlString);
- }
-
- ///
- /// Test the height ratio mode with manual width/height dimensions
- ///
- [Test]
- public void GetCropUrl_WidthHeightRatioModeTest()
- {
- var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 300, HeightRatio = 0.5m });
- Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&heightratio=0.5&width=300", urlString);
- }
-
- ///
- /// Test the height ratio mode with width/height dimensions
- ///
- [Test]
- public void GetCropUrl_HeightWidthRatioModeTest()
- {
- var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Height = 150, WidthRatio = 2 });
- Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&widthratio=2&height=150", urlString);
+ Assert.AreEqual("?cc=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100", urlString);
}
///
@@ -116,11 +86,11 @@ public void GetCropUrl_SpecifiedCropModeTest()
var urlStringMax = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = ImageCropMode.Max, Width = 300, Height = 150 });
var urlStringStretch = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = ImageCropMode.Stretch, Width = 300, Height = 150 });
- Assert.AreEqual(MediaPath + "?mode=min&width=300&height=150", urlStringMin);
- Assert.AreEqual(MediaPath + "?mode=boxpad&width=300&height=150", urlStringBoxPad);
- Assert.AreEqual(MediaPath + "?mode=pad&width=300&height=150", urlStringPad);
- Assert.AreEqual(MediaPath + "?mode=max&width=300&height=150", urlStringMax);
- Assert.AreEqual(MediaPath + "?mode=stretch&width=300&height=150", urlStringStretch);
+ Assert.AreEqual(MediaPath + "?rmode=min&width=300&height=150", urlStringMin);
+ Assert.AreEqual(MediaPath + "?rmode=boxpad&width=300&height=150", urlStringBoxPad);
+ Assert.AreEqual(MediaPath + "?rmode=pad&width=300&height=150", urlStringPad);
+ Assert.AreEqual(MediaPath + "?rmode=max&width=300&height=150", urlStringMax);
+ Assert.AreEqual(MediaPath + "?rmode=stretch&width=300&height=150", urlStringStretch);
}
///
@@ -130,7 +100,7 @@ public void GetCropUrl_SpecifiedCropModeTest()
public void GetCropUrl_UploadTypeTest()
{
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = ImageCropMode.Crop, ImageCropAnchor = ImageCropAnchor.Center, Width = 100, Height = 270 });
- Assert.AreEqual(MediaPath + "?mode=crop&anchor=center&width=100&height=270", urlString);
+ Assert.AreEqual(MediaPath + "?rmode=crop&ranchor=center&width=100&height=270", urlString);
}
///
@@ -139,28 +109,8 @@ public void GetCropUrl_UploadTypeTest()
[Test]
public void GetCropUrl_PreferFocalPointCenter()
{
- var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 300, Height = 150 });
- Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=300&height=150", urlString);
- }
-
- ///
- /// Test to check if height ratio is returned for a predefined crop without coordinates and focal point in centre when a width parameter is passed
- ///
- [Test]
- public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidth()
- {
- var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 200, HeightRatio = 0.5962962962962962962962962963m });
- Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString);
- }
-
- ///
- /// Test to check if height ratio is returned for a predefined crop without coordinates and focal point is custom when a width parameter is passed
- ///
- [Test]
- public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPoint()
- {
- var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus2, Width = 200, HeightRatio = 0.5962962962962962962962962963m });
- Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString);
+ var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Width = 300, Height = 150 });
+ Assert.AreEqual(MediaPath + "?width=300&height=150", urlString);
}
///
@@ -170,17 +120,7 @@ public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPoint()
public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPointIgnore()
{
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus2, Width = 270, Height = 161 });
- Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&width=270&height=161", urlString);
- }
-
- ///
- /// Test to check if width ratio is returned for a predefined crop without coordinates and focal point in centre when a height parameter is passed
- ///
- [Test]
- public void GetCropUrl_PreDefinedCropNoCoordinatesWithHeight()
- {
- var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Height = 200, WidthRatio = 1.6770186335403726708074534161m });
- Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&widthratio=1.6770186335403726708074534161&height=200", urlString);
+ Assert.AreEqual(MediaPath + "?rxy=0.4275,0.41&width=270&height=161", urlString);
}
///
@@ -189,8 +129,8 @@ public void GetCropUrl_PreDefinedCropNoCoordinatesWithHeight()
[Test]
public void GetCropUrl_WidthOnlyParameter()
{
- var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 200 });
- Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=200", urlString);
+ var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Width = 200 });
+ Assert.AreEqual(MediaPath + "?width=200", urlString);
}
///
@@ -199,8 +139,8 @@ public void GetCropUrl_WidthOnlyParameter()
[Test]
public void GetCropUrl_HeightOnlyParameter()
{
- var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Height = 200 });
- Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&height=200", urlString);
+ var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Height = 200 });
+ Assert.AreEqual(MediaPath + "?height=200", urlString);
}
///
@@ -210,7 +150,7 @@ public void GetCropUrl_HeightOnlyParameter()
public void GetCropUrl_BackgroundColorParameter()
{
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = ImageCropMode.Pad, Width = 400, Height = 400, FurtherOptions = "&bgcolor=fff" });
- Assert.AreEqual(MediaPath + "?mode=pad&width=400&height=400&bgcolor=fff", urlString);
+ Assert.AreEqual(MediaPath + "?rmode=pad&width=400&height=400&bgcolor=fff", urlString);
}
}
}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs
new file mode 100644
index 000000000000..b3b1e64f947c
--- /dev/null
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs
@@ -0,0 +1,114 @@
+// Copyright (c) Umbraco.
+// See LICENSE for more details.
+
+using System.Collections.Generic;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Cms.Core.Media;
+using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
+using Umbraco.Extensions;
+
+namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Extensions
+{
+ [TestFixture]
+ public class ImageCropperTemplateCoreExtensionsTests
+ {
+ [Test]
+ public void GetCropUrl_WithCropSpecifiedButNotFound_ReturnsNull()
+ {
+ var imageUrl = "/test.jpg";
+ Mock imageUrlGenerator = CreateMockImageUrlGenerator();
+ var result = imageUrl.GetCropUrl(
+ imageUrlGenerator.Object,
+ new ImageCropperValue { },
+ imageCropMode: ImageCropMode.Crop,
+ cropAlias: "Missing");
+
+ Assert.IsNull(result);
+ }
+
+ [Test]
+ public void GetCropUrl_WithCropSpecifiedAndUsingCropDimensions_CallsImageGeneratorWithCorrectParameters()
+ {
+ var imageUrl = "/test.jpg";
+ Mock imageUrlGenerator = CreateMockImageUrlGenerator();
+ var result = imageUrl.GetCropUrl(
+ imageUrlGenerator.Object,
+ CreateImageCropperValueWithCrops(),
+ imageCropMode: ImageCropMode.Crop,
+ cropAlias: "TestCrop",
+ useCropDimensions: true);
+
+ imageUrlGenerator
+ .Verify(x => x.GetImageUrl(
+ It.Is(y => y.Width == 100 &&
+ y.Height == 200)));
+ }
+
+ [Test]
+ public void GetCropUrl_WithCropSpecifiedAndWidthAndHeightProvided_CallsImageGeneratorWithCorrectParameters()
+ {
+ var imageUrl = "/test.jpg";
+ Mock imageUrlGenerator = CreateMockImageUrlGenerator();
+ var result = imageUrl.GetCropUrl(
+ imageUrlGenerator.Object,
+ CreateImageCropperValueWithCrops(),
+ imageCropMode: ImageCropMode.Crop,
+ cropAlias: "TestCrop",
+ width: 50,
+ height: 80);
+
+ imageUrlGenerator
+ .Verify(x => x.GetImageUrl(
+ It.Is(y => y.Width == 50 &&
+ y.Height == 80)));
+ }
+
+ [Test]
+ public void GetCropUrl_WithCropSpecifiedAndWidthOnlyProvided_CallsImageGeneratorWithCorrectParameters()
+ {
+ var imageUrl = "/test.jpg";
+ Mock imageUrlGenerator = CreateMockImageUrlGenerator();
+ var result = imageUrl.GetCropUrl(
+ imageUrlGenerator.Object,
+ CreateImageCropperValueWithCrops(),
+ imageCropMode: ImageCropMode.Crop,
+ cropAlias: "TestCrop",
+ width: 50);
+
+ imageUrlGenerator
+ .Verify(x => x.GetImageUrl(
+ It.Is(y => y.Width == 50 &&
+ y.Height == 100)));
+ }
+
+ [Test]
+ public void GetCropUrl_WithCropSpecifiedAndHeightOnlyProvided_CallsImageGeneratorWithCorrectParameters()
+ {
+ var imageUrl = "/test.jpg";
+ Mock imageUrlGenerator = CreateMockImageUrlGenerator();
+ var result = imageUrl.GetCropUrl(
+ imageUrlGenerator.Object,
+ CreateImageCropperValueWithCrops(),
+ imageCropMode: ImageCropMode.Crop,
+ cropAlias: "TestCrop",
+ height: 50);
+
+ imageUrlGenerator
+ .Verify(x => x.GetImageUrl(
+ It.Is(y => y.Width == 25 &&
+ y.Height == 50)));
+ }
+
+ private static Mock CreateMockImageUrlGenerator() => new Mock();
+
+ private static ImageCropperValue CreateImageCropperValueWithCrops() => new ImageCropperValue
+ {
+ Crops = new List
+ {
+ new ImageCropperValue.ImageCropperCrop { Alias = "TestCrop", Width = 100, Height = 200 },
+ }
+ };
+ }
+}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs
index 1873b30c9961..ce5e62d799f1 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs
@@ -1,8 +1,10 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
+using System;
using System.Collections.Generic;
using System.Globalization;
+using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -130,21 +132,21 @@ public void GetCropUrl_CropAliasIgnoreWidthHeightTest()
public void GetCropUrl_WidthHeightTest()
{
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300);
- Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=200&h=300", urlString);
+ Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=200&h=300", urlString);
}
[Test]
public void GetCropUrl_FocalPointTest()
{
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "thumb", preferFocalPoint: true, useCropDimensions: true);
- Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=100&h=100", urlString);
+ Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=100&h=100", urlString);
}
[Test]
public void GetCropUrlFurtherOptionsTest()
{
- var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300, furtherOptions: "&filter=comic&roundedcorners=radius-26|bgcolor-fff");
- Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=200&h=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString);
+ var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300, furtherOptions: "filter=comic&roundedcorners=radius-26|bgcolor-fff");
+ Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=200&h=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString);
}
///
@@ -174,8 +176,8 @@ public void GetBaseCropUrlFromModelTest()
[Test]
public void GetCropUrl_CropAliasHeightRatioModeTest()
{
- var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true, ratioMode: ImageCropRatioMode.Height);
- Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&hr=1&w=100", urlString);
+ var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true);
+ Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&w=100&h=100", urlString);
}
///
@@ -184,8 +186,8 @@ public void GetCropUrl_CropAliasHeightRatioModeTest()
[Test]
public void GetCropUrl_WidthHeightRatioModeTest()
{
- var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode: ImageCropRatioMode.Height);
- Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&hr=0.5&w=300", urlString);
+ var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150);
+ Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=300&h=150", urlString);
}
///
@@ -194,8 +196,8 @@ public void GetCropUrl_WidthHeightRatioModeTest()
[Test]
public void GetCropUrl_HeightWidthRatioModeTest()
{
- var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode: ImageCropRatioMode.Width);
- Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&wr=2&h=150", urlString);
+ var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150);
+ Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=300&h=150", urlString);
}
///
@@ -236,7 +238,7 @@ public void GetCropUrl_PreferFocalPointCenter()
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\":\"thumb\",\"width\": 100,\"height\": 100,\"coordinates\": {\"x1\": 0.58729977382575338,\"y1\": 0.055768992440203169,\"x2\": 0,\"y2\": 0.32457553600198386}}]}";
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, width: 300, height: 150, preferFocalPoint: true);
- Assert.AreEqual(MediaPath + "?m=defaultcrop&w=300&h=150", urlString);
+ Assert.AreEqual(MediaPath + "?w=300&h=150", urlString);
}
///
@@ -248,7 +250,7 @@ public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidth()
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200);
- Assert.AreEqual(MediaPath + "?m=defaultcrop&hr=0.5962962962962962962962962963&w=200", urlString);
+ Assert.AreEqual(MediaPath + "?w=200&h=119", urlString);
}
///
@@ -260,7 +262,7 @@ public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPoint()
const string cropperJson = "{\"focalPoint\": {\"left\": 0.4275,\"top\": 0.41},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200);
- Assert.AreEqual(MediaPath + "?f=0.41x0.4275&hr=0.5962962962962962962962962963&w=200", urlString);
+ Assert.AreEqual(MediaPath + "?f=0.41,0.4275&w=200&h=119", urlString);
}
///
@@ -272,7 +274,7 @@ public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPointIgnore()
const string cropperJson = "{\"focalPoint\": {\"left\": 0.4275,\"top\": 0.41},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200, useCropDimensions: true);
- Assert.AreEqual(MediaPath + "?f=0.41x0.4275&w=270&h=161", urlString);
+ Assert.AreEqual(MediaPath + "?f=0.41,0.4275&w=270&h=161", urlString);
}
///
@@ -284,7 +286,7 @@ public void GetCropUrl_PreDefinedCropNoCoordinatesWithHeight()
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", height: 200);
- Assert.AreEqual(MediaPath + "?m=defaultcrop&wr=1.6770186335403726708074534161&h=200", urlString);
+ Assert.AreEqual(MediaPath + "?w=335&h=200", urlString);
}
///
@@ -296,7 +298,7 @@ public void GetCropUrl_WidthOnlyParameter()
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, width: 200);
- Assert.AreEqual(MediaPath + "?m=defaultcrop&w=200", urlString);
+ Assert.AreEqual(MediaPath + "?w=200", urlString);
}
///
@@ -308,7 +310,7 @@ public void GetCropUrl_HeightOnlyParameter()
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, height: 200);
- Assert.AreEqual(MediaPath + "?m=defaultcrop&h=200", urlString);
+ Assert.AreEqual(MediaPath + "?h=200", urlString);
}
///
@@ -319,98 +321,88 @@ public void GetCropUrl_BackgroundColorParameter()
{
var cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"" + MediaPath + "\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
- var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), 400, 400, cropperJson, imageCropMode: ImageCropMode.Pad, furtherOptions: "&bgcolor=fff");
+ var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), 400, 400, cropperJson, imageCropMode: ImageCropMode.Pad, furtherOptions: "bgcolor=fff");
Assert.AreEqual(MediaPath + "?m=pad&w=400&h=400&bgcolor=fff", urlString);
}
internal class TestImageUrlGenerator : IImageUrlGenerator
{
- public IEnumerable SupportedImageFileTypes => new[] { "jpeg", "jpg", "gif", "bmp", "png", "tiff", "tif" };
+ public IEnumerable SupportedImageFileTypes => new[]
+ {
+ "jpeg",
+ "jpg",
+ "gif",
+ "bmp",
+ "png",
+ "tiff",
+ "tif"
+ };
public string GetImageUrl(ImageUrlGenerationOptions options)
{
- var imageProcessorUrl = new StringBuilder(options.ImageUrl ?? string.Empty);
-
- if (options.FocalPoint != null)
+ if (options == null)
{
- imageProcessorUrl.Append("?f=");
- imageProcessorUrl.Append(options.FocalPoint.Top.ToString(CultureInfo.InvariantCulture));
- imageProcessorUrl.Append("x");
- imageProcessorUrl.Append(options.FocalPoint.Left.ToString(CultureInfo.InvariantCulture));
+ return null;
}
- else if (options.Crop != null)
- {
- imageProcessorUrl.Append("?c=");
- imageProcessorUrl.Append(options.Crop.X1.ToString(CultureInfo.InvariantCulture)).Append(",");
- imageProcessorUrl.Append(options.Crop.Y1.ToString(CultureInfo.InvariantCulture)).Append(",");
- imageProcessorUrl.Append(options.Crop.X2.ToString(CultureInfo.InvariantCulture)).Append(",");
- imageProcessorUrl.Append(options.Crop.Y2.ToString(CultureInfo.InvariantCulture));
- }
- else if (options.DefaultCrop)
+
+ var imageUrl = new StringBuilder(options.ImageUrl);
+
+ bool queryStringHasStarted = false;
+ void AppendQueryString(string value)
{
- imageProcessorUrl.Append("?m=defaultcrop");
+ imageUrl.Append(queryStringHasStarted ? '&' : '?');
+ queryStringHasStarted = true;
+
+ imageUrl.Append(value);
}
- else
+ void AddQueryString(string key, params IConvertible[] values)
+ => AppendQueryString(key + '=' + string.Join(",", values.Select(x => x.ToString(CultureInfo.InvariantCulture))));
+
+ if (options.FocalPoint != null)
{
- imageProcessorUrl.Append("?m=" + options.ImageCropMode.ToString().ToLower());
- if (options.ImageCropAnchor != null)
- {
- imageProcessorUrl.Append("&a=" + options.ImageCropAnchor.ToString().ToLower());
- }
+ AddQueryString("f", options.FocalPoint.Top, options.FocalPoint.Left);
}
-
- var hasFormat = options.FurtherOptions != null && options.FurtherOptions.InvariantContains("&f=");
- if (options.Quality != null && hasFormat == false)
+ else if (options.Crop != null)
{
- imageProcessorUrl.Append("&q=" + options.Quality);
+ AddQueryString("c", options.Crop.Left, options.Crop.Top, options.Crop.Right, options.Crop.Bottom);
}
- if (options.HeightRatio != null)
+ if (options.ImageCropMode.HasValue)
{
- imageProcessorUrl.Append("&hr=" + options.HeightRatio.Value.ToString(CultureInfo.InvariantCulture));
+ AddQueryString("m", options.ImageCropMode.Value.ToString().ToLowerInvariant());
}
- if (options.WidthRatio != null)
+ if (options.ImageCropAnchor.HasValue)
{
- imageProcessorUrl.Append("&wr=" + options.WidthRatio.Value.ToString(CultureInfo.InvariantCulture));
+ AddQueryString("a", options.ImageCropAnchor.Value.ToString().ToLowerInvariant());
}
if (options.Width != null)
{
- imageProcessorUrl.Append("&w=" + options.Width);
+ AddQueryString("w", options.Width.Value);
}
if (options.Height != null)
{
- imageProcessorUrl.Append("&h=" + options.Height);
+ AddQueryString("h", options.Height.Value);
}
-
- if (options.UpScale == false)
+
+ if (options.Quality.HasValue)
{
- imageProcessorUrl.Append("&u=no");
- }
-
- if (options.AnimationProcessMode != null)
- {
- imageProcessorUrl.Append("&apm=" + options.AnimationProcessMode);
+ AddQueryString("q", options.Quality.Value);
}
if (options.FurtherOptions != null)
{
- imageProcessorUrl.Append(options.FurtherOptions);
- }
-
- if (options.Quality != null && hasFormat)
- {
- imageProcessorUrl.Append("&q=" + options.Quality);
+ AppendQueryString(options.FurtherOptions.TrimStart('?', '&'));
}
if (options.CacheBusterValue != null)
{
- imageProcessorUrl.Append("&r=").Append(options.CacheBusterValue);
+ AddQueryString("r", options.CacheBusterValue);
}
- return imageProcessorUrl.ToString();
+ return imageUrl.ToString();
}
}
}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageProcessors/CropWebProcessorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageProcessors/CropWebProcessorTests.cs
new file mode 100644
index 000000000000..2c508d97d200
--- /dev/null
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageProcessors/CropWebProcessorTests.cs
@@ -0,0 +1,80 @@
+// Copyright (c) Umbraco.
+// See LICENSE for more details.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Reflection;
+using NUnit.Framework;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Web;
+using SixLabors.ImageSharp.Web.Commands;
+using SixLabors.ImageSharp.Web.Commands.Converters;
+using Umbraco.Cms.Web.Common.ImageProcessors;
+
+namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.ImageProcessors
+{
+ [TestFixture]
+ public class CropWebProcessorTests
+ {
+ [Test]
+ public void CropWebProcessor_CropsImage()
+ {
+ var converters = new List
+ {
+ CreateArrayConverterOfFloat(),
+ CreateSimpleCommandConverterOfFloat(),
+ };
+
+ var parser = new CommandParser(converters);
+ CultureInfo culture = CultureInfo.InvariantCulture;
+
+ var commands = new Dictionary
+ {
+ { CropWebProcessor.Coordinates, "0.1,0.2,0.1,0.4" }, // left, top, right, bottom
+ };
+
+ using var image = new Image(50, 80);
+ using FormattedImage formatted = CreateFormattedImage(image, PngFormat.Instance);
+ new CropWebProcessor().Process(formatted, null, commands, parser, culture);
+
+ Assert.AreEqual(40, image.Width); // Cropped 5 pixels from each side.
+ Assert.AreEqual(32, image.Height); // Cropped 16 pixels from the top and 32 from the bottom.
+ }
+
+ private static ICommandConverter CreateArrayConverterOfFloat()
+ {
+ // ImageSharp.Web's ArrayConverter is internal, so we need to use reflection to instantiate.
+ var type = Type.GetType("SixLabors.ImageSharp.Web.Commands.Converters.ArrayConverter`1, SixLabors.ImageSharp.Web");
+ Type[] typeArgs = { typeof(float) };
+ Type genericType = type.MakeGenericType(typeArgs);
+ return (ICommandConverter)Activator.CreateInstance(genericType);
+ }
+
+ private static ICommandConverter CreateSimpleCommandConverterOfFloat()
+ {
+ // ImageSharp.Web's SimpleCommandConverter is internal, so we need to use reflection to instantiate.
+ var type = Type.GetType("SixLabors.ImageSharp.Web.Commands.Converters.SimpleCommandConverter`1, SixLabors.ImageSharp.Web");
+ Type[] typeArgs = { typeof(float) };
+ Type genericType = type.MakeGenericType(typeArgs);
+ return (ICommandConverter)Activator.CreateInstance(genericType);
+ }
+
+ private FormattedImage CreateFormattedImage(Image image, PngFormat format)
+ {
+ // Again, the constructor of FormattedImage useful for tests is internal, so we need to use reflection.
+ Type type = typeof(FormattedImage);
+ var instance = type.Assembly.CreateInstance(
+ type.FullName,
+ false,
+ BindingFlags.Instance | BindingFlags.NonPublic,
+ null,
+ new object[] { image, format },
+ null,
+ null);
+ return (FormattedImage)instance;
+ }
+ }
+}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs
index c3fb203ec121..c822b61d6786 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs
@@ -365,7 +365,7 @@ internal Task> GetServerVariablesAsync()
},
{
"imageUrlGeneratorApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl(
- controller => controller.GetCropUrl(null, null, null, null, null))
+ controller => controller.GetCropUrl(null, null, null, null))
},
{
"elementTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl(
diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs
index 1d72c80ad8d7..7546fdf38de2 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs
@@ -1,4 +1,4 @@
-using Umbraco.Cms.Core.Media;
+using Umbraco.Cms.Core.Media;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Web.Common.Attributes;
using Constants = Umbraco.Cms.Core.Constants;
@@ -21,20 +21,13 @@ public class ImageUrlGeneratorController : UmbracoAuthorizedJsonController
{
private readonly IImageUrlGenerator _imageUrlGenerator;
- public ImageUrlGeneratorController(IImageUrlGenerator imageUrlGenerator)
- {
- _imageUrlGenerator = imageUrlGenerator;
- }
+ public ImageUrlGeneratorController(IImageUrlGenerator imageUrlGenerator) => _imageUrlGenerator = imageUrlGenerator;
- public string GetCropUrl(string mediaPath, int? width = null, int? height = null, ImageCropMode? imageCropMode = null, string animationProcessMode = null)
+ public string GetCropUrl(string mediaPath, int? width = null, int? height = null, ImageCropMode? imageCropMode = null) => _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(mediaPath)
{
- return _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(mediaPath)
- {
- Width = width,
- Height = height,
- ImageCropMode = imageCropMode,
- AnimationProcessMode = animationProcessMode
- });
- }
+ Width = width,
+ Height = height,
+ ImageCropMode = imageCropMode
+ });
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs
index 24135bcbe63b..564d0dcdd93e 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.IO;
@@ -76,9 +76,7 @@ public IActionResult GetResized(string imagePath, int width)
var rnd = imageLastModified.HasValue ? $"&rnd={imageLastModified:yyyyMMddHHmmss}" : null;
var imageUrl = _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(imagePath)
{
- UpScale = false,
Width = width,
- AnimationProcessMode = "first",
ImageCropMode = ImageCropMode.Max,
CacheBusterValue = rnd
});
@@ -94,9 +92,7 @@ public IActionResult GetResized(string imagePath, int width)
///
///
///
- ///
///
- ///
///
///
/// If there is no media, image property or image file is found then this will return not found.
@@ -106,9 +102,7 @@ public string GetProcessedImageUrl(string imagePath,
int? height = null,
decimal? focalPointLeft = null,
decimal? focalPointTop = null,
- string animationProcessMode = "first",
ImageCropMode mode = ImageCropMode.Max,
- bool upscale = false,
string cacheBusterValue = "",
decimal? cropX1 = null,
decimal? cropX2 = null,
@@ -116,45 +110,24 @@ public string GetProcessedImageUrl(string imagePath,
decimal? cropY2 = null
)
{
-
-
var options = new ImageUrlGenerationOptions(imagePath)
{
- AnimationProcessMode = animationProcessMode,
- CacheBusterValue = cacheBusterValue,
+ Width = width,
Height = height,
ImageCropMode = mode,
- UpScale = upscale,
- Width = width,
- Crop = (cropX1.HasValue && cropX2.HasValue && cropY1.HasValue && cropY2.HasValue) ? new ImageUrlGenerationOptions.CropCoordinates(cropX1.Value, cropY1.Value, cropX2.Value, cropY2.Value) : null,
- FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(focalPointTop.GetValueOrDefault(0.5m), focalPointLeft.GetValueOrDefault(0.5m)),
+ CacheBusterValue = cacheBusterValue
};
+
if (focalPointLeft.HasValue && focalPointTop.HasValue)
{
- options.FocalPoint =
- new ImageUrlGenerationOptions.FocalPointPosition(focalPointTop.Value, focalPointLeft.Value);
+ options.FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(focalPointLeft.Value, focalPointTop.Value);
+ }
+ else if (cropX1.HasValue && cropX2.HasValue && cropY1.HasValue && cropY2.HasValue)
+ {
+ options.Crop = new ImageUrlGenerationOptions.CropCoordinates(cropX1.Value, cropY1.Value, cropX2.Value, cropY2.Value);
}
return _imageUrlGenerator.GetImageUrl(options);
}
-
- public class FocalPointPositionModel
- {
- public decimal Left { get; set; }
- public decimal Top { get; set; }
- }
-
- ///
- /// The bounds of the crop within the original image, in whatever units the registered
- /// IImageUrlGenerator uses, typically a percentage between 0 and 100.
- ///
- public class CropCoordinatesModel
- {
-
- public decimal X1 { get; set; }
- public decimal Y1 { get; set; }
- public decimal X2 { get; set;}
- public decimal Y2 { get; set;}
- }
}
}
diff --git a/src/Umbraco.Web.Common/DependencyInjection/ImageSharpConfigurationOptions.cs b/src/Umbraco.Web.Common/DependencyInjection/ImageSharpConfigurationOptions.cs
new file mode 100644
index 000000000000..628345dcd67a
--- /dev/null
+++ b/src/Umbraco.Web.Common/DependencyInjection/ImageSharpConfigurationOptions.cs
@@ -0,0 +1,30 @@
+using Microsoft.Extensions.Options;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Web.Middleware;
+
+namespace Umbraco.Cms.Web.Common.DependencyInjection
+{
+ ///
+ /// Configures the ImageSharp middleware options to use the registered configuration.
+ ///
+ ///
+ public sealed class ImageSharpConfigurationOptions : IConfigureOptions
+ {
+ ///
+ /// The ImageSharp configuration.
+ ///
+ private readonly Configuration _configuration;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The ImageSharp configuration.
+ public ImageSharpConfigurationOptions(Configuration configuration) => _configuration = configuration;
+
+ ///
+ /// Invoked to configure a instance.
+ ///
+ /// The options instance to configure.
+ public void Configure(ImageSharpMiddlewareOptions options) => options.Configuration = _configuration;
+ }
+}
diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs
index 96bf7017cb0e..280d48f64bd9 100644
--- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs
+++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs
@@ -2,14 +2,16 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-using SixLabors.ImageSharp.Memory;
+using Microsoft.Extensions.Options;
using SixLabors.ImageSharp.Web.Caching;
using SixLabors.ImageSharp.Web.Commands;
using SixLabors.ImageSharp.Web.DependencyInjection;
+using SixLabors.ImageSharp.Web.Middleware;
using SixLabors.ImageSharp.Web.Processors;
-using SixLabors.ImageSharp.Web.Providers;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
+using Umbraco.Cms.Web.Common.DependencyInjection;
+using Umbraco.Cms.Web.Common.ImageProcessors;
namespace Umbraco.Extensions
{
@@ -20,56 +22,44 @@ public static partial class UmbracoBuilderExtensions
///
public static IServiceCollection AddUmbracoImageSharp(this IUmbracoBuilder builder)
{
- IConfiguration configuration = builder.Config;
- IServiceCollection services = builder.Services;
-
- ImagingSettings imagingSettings = configuration.GetSection(Cms.Core.Constants.Configuration.ConfigImaging)
+ ImagingSettings imagingSettings = builder.Config.GetSection(Cms.Core.Constants.Configuration.ConfigImaging)
.Get() ?? new ImagingSettings();
- services.AddImageSharp(options =>
+ builder.Services.AddImageSharp(options =>
{
- options.Configuration = SixLabors.ImageSharp.Configuration.Default;
+ // The configuration is set using ImageSharpConfigurationOptions
options.BrowserMaxAge = imagingSettings.Cache.BrowserMaxAge;
options.CacheMaxAge = imagingSettings.Cache.CacheMaxAge;
options.CachedNameLength = imagingSettings.Cache.CachedNameLength;
+
+ // Use configurable maximum width and height (overwrite ImageSharps default)
options.OnParseCommandsAsync = context =>
{
- RemoveIntParamenterIfValueGreatherThen(context.Commands, ResizeWebProcessor.Width, imagingSettings.Resize.MaxWidth);
- RemoveIntParamenterIfValueGreatherThen(context.Commands, ResizeWebProcessor.Height, imagingSettings.Resize.MaxHeight);
+ if (context.Commands.Count == 0)
+ {
+ return Task.CompletedTask;
+ }
+
+ uint width = context.Parser.ParseValue(context.Commands.GetValueOrDefault(ResizeWebProcessor.Width), context.Culture);
+ uint height = context.Parser.ParseValue(context.Commands.GetValueOrDefault(ResizeWebProcessor.Height), context.Culture);
+ if (width > imagingSettings.Resize.MaxWidth || height > imagingSettings.Resize.MaxHeight)
+ {
+ context.Commands.Remove(ResizeWebProcessor.Width);
+ context.Commands.Remove(ResizeWebProcessor.Height);
+ }
return Task.CompletedTask;
};
- options.OnBeforeSaveAsync = _ => Task.CompletedTask;
- options.OnProcessedAsync = _ => Task.CompletedTask;
- options.OnPrepareResponseAsync = _ => Task.CompletedTask;
})
- .SetRequestParser()
- .Configure(options =>
- {
- options.CacheFolder = imagingSettings.Cache.CacheFolder;
- })
- .SetCache()
- .SetCacheHash()
- .AddProvider()
- .AddProcessor()
- .AddProcessor()
- .AddProcessor();
+ .Configure(options => options.CacheFolder = imagingSettings.Cache.CacheFolder)
+ // We need to add CropWebProcessor before ResizeWebProcessor (until https://github.com/SixLabors/ImageSharp.Web/issues/182 is fixed)
+ .RemoveProcessor()
+ .AddProcessor()
+ .AddProcessor();
- return services;
- }
+ builder.Services.AddTransient, ImageSharpConfigurationOptions>();
- private static void RemoveIntParamenterIfValueGreatherThen(IDictionary commands, string parameter, int maxValue)
- {
- if (commands.TryGetValue(parameter, out var command))
- {
- if (int.TryParse(command, out var i))
- {
- if (i > maxValue)
- {
- commands.Remove(parameter);
- }
- }
- }
+ return builder.Services;
}
}
}
diff --git a/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs b/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs
index 94e24a502762..38f6f472356d 100644
--- a/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs
+++ b/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.Media;
using Umbraco.Cms.Core.Models;
@@ -11,25 +11,17 @@ namespace Umbraco.Extensions
{
public static class FriendlyImageCropperTemplateExtensions
{
- private static IImageUrlGenerator ImageUrlGenerator { get; } =
- StaticServiceProvider.Instance.GetRequiredService();
+ private static IImageUrlGenerator ImageUrlGenerator { get; } = StaticServiceProvider.Instance.GetRequiredService();
- private static IPublishedValueFallback PublishedValueFallback { get; } =
- StaticServiceProvider.Instance.GetRequiredService();
-
- private static IPublishedUrlProvider PublishedUrlProvider { get; } =
- StaticServiceProvider.Instance.GetRequiredService();
+ private static IPublishedValueFallback PublishedValueFallback { get; } = StaticServiceProvider.Instance.GetRequiredService();
+ private static IPublishedUrlProvider PublishedUrlProvider { get; } = StaticServiceProvider.Instance.GetRequiredService();
///
- /// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item
+ /// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item.
///
- ///
- /// The IPublishedContent item.
- ///
- ///
- /// The crop alias e.g. thumbnail
- ///
+ /// The IPublishedContent item.
+ /// The crop alias e.g. thumbnail.
///
/// The URL of the cropped image.
///
@@ -57,17 +49,11 @@ public static string GetCropUrl(
=> ImageCropperTemplateCoreExtensions.GetCropUrl(mediaItem, imageCropperValue, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider);
///
- /// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper Json data on the IPublishedContent item.
+ /// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper JSON data on the IPublishedContent item.
///
- ///
- /// The IPublishedContent item.
- ///
- ///
- /// The property alias of the property containing the Json data e.g. umbracoFile
- ///
- ///
- /// The crop alias e.g. thumbnail
- ///
+ /// The IPublishedContent item.
+ /// The property alias of the property containing the JSON data e.g. umbracoFile.
+ /// The crop alias e.g. thumbnail.
///
/// The URL of the cropped image.
///
@@ -83,53 +69,21 @@ public static string GetCropUrl(this MediaWithCrops mediaWithCrops, string prope
///
/// Gets the underlying image processing service URL from the IPublishedContent item.
///
- ///
- /// The IPublishedContent item.
- ///
- ///
- /// The width of the output image.
- ///
- ///
- /// The height of the output image.
- ///
- ///
- /// Property alias of the property containing the Json data.
- ///
- ///
- /// The crop alias.
- ///
- ///
- /// Quality percentage of the output image.
- ///
- ///
- /// The image crop mode.
- ///
- ///
- /// The image crop anchor.
- ///
- ///
- /// Use focal point, to generate an output image using the focal point instead of the predefined crop
- ///
- ///
- /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.
- ///
- ///
- /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated
- ///
- ///
- /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
- ///
- ///
- ///
- ///
- ///
- /// Use a dimension as a ratio
- ///
- ///
- /// If the image should be upscaled to requested dimensions
- ///
+ /// The IPublishedContent item.
+ /// The width of the output image.
+ /// The height of the output image.
+ /// Property alias of the property containing the JSON data.
+ /// The crop alias.
+ /// Quality percentage of the output image.
+ /// The image crop mode.
+ /// The image crop anchor.
+ /// Use focal point, to generate an output image using the focal point instead of the predefined crop.
+ /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.
+ /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated.
+ /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
+ ///
///
/// The URL of the cropped image.
///
@@ -145,9 +99,7 @@ public static string GetCropUrl(
bool preferFocalPoint = false,
bool useCropDimensions = false,
bool cacheBuster = true,
- string furtherOptions = null,
- ImageCropRatioMode? ratioMode = null,
- bool upScale = true)
+ string furtherOptions = null)
=> mediaItem.GetCropUrl(
ImageUrlGenerator,
PublishedValueFallback,
@@ -162,63 +114,29 @@ public static string GetCropUrl(
preferFocalPoint,
useCropDimensions,
cacheBuster,
- furtherOptions,
- ratioMode,
- upScale
+ furtherOptions
);
///
/// Gets the underlying image processing service URL from the image path.
///
- ///
- /// The image URL.
- ///
- ///
- /// The width of the output image.
- ///
- ///
- /// The height of the output image.
- ///
- ///
- /// The Json data from the Umbraco Core Image Cropper property editor
- ///
- ///
- /// The crop alias.
- ///
- ///
- /// Quality percentage of the output image.
- ///
- ///
- /// The image crop mode.
- ///
- ///
- /// The image crop anchor.
- ///
- ///
- /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one
- ///
- ///
- /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters
- ///
- ///
- /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated
- ///
- ///
- /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
- ///
- ///
- ///
- ///
- ///
- /// Use a dimension as a ratio
- ///
- ///
- /// If the image should be upscaled to requested dimensions
- ///
+ /// The image URL.
+ /// The width of the output image.
+ /// The height of the output image.
+ /// The JSON data from the Umbraco Core Image Cropper property editor.
+ /// The crop alias.
+ /// Quality percentage of the output image.
+ /// The image crop mode.
+ /// The image crop anchor.
+ /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one.
+ /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.
+ /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated.
+ /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
+ ///
///
- /// The the URL of the cropped image.
+ /// The URL of the cropped image.
///
public static string GetCropUrl(
this string imageUrl,
@@ -232,9 +150,7 @@ public static string GetCropUrl(
bool preferFocalPoint = false,
bool useCropDimensions = false,
string cacheBusterValue = null,
- string furtherOptions = null,
- ImageCropRatioMode? ratioMode = null,
- bool upScale = true)
+ string furtherOptions = null)
=> imageUrl.GetCropUrl(
ImageUrlGenerator,
width,
@@ -247,61 +163,29 @@ public static string GetCropUrl(
preferFocalPoint,
useCropDimensions,
cacheBusterValue,
- furtherOptions,
- ratioMode,
- upScale
- );
+ furtherOptions
+ );
///
/// Gets the underlying image processing service URL from the image path.
///
- ///
- /// The image URL.
- ///
- ///
- ///
- /// The width of the output image.
- ///
- ///
- /// The height of the output image.
- ///
- ///
- /// The crop alias.
- ///
- ///
- /// Quality percentage of the output image.
- ///
- ///
- /// The image crop mode.
- ///
- ///
- /// The image crop anchor.
- ///
- ///
- /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one
- ///
- ///
- /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters
- ///
- ///
- /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated
- ///
- ///
- /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
- ///
- ///
- ///
- ///
- ///
- /// Use a dimension as a ratio
- ///
- ///
- /// If the image should be upscaled to requested dimensions
- ///
+ /// The image URL.
+ /// The crop data set.
+ /// The width of the output image.
+ /// The height of the output image.
+ /// The crop alias.
+ /// Quality percentage of the output image.
+ /// The image crop mode.
+ /// The image crop anchor.
+ /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one.
+ /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.
+ /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated.
+ /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
+ ///
///
- /// The the URL of the cropped image.
+ /// The URL of the cropped image.
///
public static string GetCropUrl(
this string imageUrl,
@@ -315,10 +199,7 @@ public static string GetCropUrl(
bool preferFocalPoint = false,
bool useCropDimensions = false,
string cacheBusterValue = null,
- string furtherOptions = null,
- ImageCropRatioMode? ratioMode = null,
- bool upScale = true,
- string animationProcessMode = null)
+ string furtherOptions = null)
=> imageUrl.GetCropUrl(
ImageUrlGenerator,
cropDataSet,
@@ -330,10 +211,7 @@ public static string GetCropUrl(
preferFocalPoint,
useCropDimensions,
cacheBusterValue,
- furtherOptions,
- ratioMode,
- upScale,
- animationProcessMode
+ furtherOptions
);
@@ -341,10 +219,6 @@ public static string GetCropUrl(
public static string GetLocalCropUrl(
this MediaWithCrops mediaWithCrops,
string alias,
- string cacheBusterValue = null)
- {
- return mediaWithCrops.LocalCrops.Src +
- mediaWithCrops.LocalCrops.GetCropUrl(alias, ImageUrlGenerator, cacheBusterValue: cacheBusterValue);
- }
+ string cacheBusterValue = null) => mediaWithCrops.LocalCrops.Src + mediaWithCrops.LocalCrops.GetCropUrl(alias, ImageUrlGenerator, cacheBusterValue: cacheBusterValue);
}
}
diff --git a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs
index 879a70cdb594..ae367b1cf951 100644
--- a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs
+++ b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using Newtonsoft.Json.Linq;
using Umbraco.Cms.Core;
@@ -13,17 +13,13 @@ namespace Umbraco.Extensions
public static class ImageCropperTemplateCoreExtensions
{
///
- /// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item
+ /// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item.
///
- ///
- /// The IPublishedContent item.
- ///
- ///
- /// The crop alias e.g. thumbnail
- ///
- /// The image url generator.
+ /// The IPublishedContent item.
+ /// The crop alias e.g. thumbnail.
+ /// The image URL generator.
/// The published value fallback.
- /// The published url provider.
+ /// The published URL provider.
///
/// The URL of the cropped image.
///
@@ -32,20 +28,14 @@ public static string GetCropUrl(
string cropAlias,
IImageUrlGenerator imageUrlGenerator,
IPublishedValueFallback publishedValueFallback,
- IPublishedUrlProvider publishedUrlProvider)
- {
- return mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true);
- }
+ IPublishedUrlProvider publishedUrlProvider) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true);
public static string GetCropUrl(
this MediaWithCrops mediaWithCrops,
string cropAlias,
IImageUrlGenerator imageUrlGenerator,
IPublishedValueFallback publishedValueFallback,
- IPublishedUrlProvider publishedUrlProvider)
- {
- return mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true);
- }
+ IPublishedUrlProvider publishedUrlProvider) => mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true);
///
/// Gets the crop URL by using only the specified .
@@ -54,6 +44,8 @@ public static string GetCropUrl(
/// The image cropper value.
/// The crop alias.
/// The image URL generator.
+ /// The published value fallback.
+ /// The published URL provider.
///
/// The image crop URL.
///
@@ -63,26 +55,17 @@ public static string GetCropUrl(
string cropAlias,
IImageUrlGenerator imageUrlGenerator,
IPublishedValueFallback publishedValueFallback,
- IPublishedUrlProvider publishedUrlProvider)
- {
- return mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, imageCropperValue, true, cropAlias: cropAlias, useCropDimensions: true);
- }
+ IPublishedUrlProvider publishedUrlProvider) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, imageCropperValue, true, cropAlias: cropAlias, useCropDimensions: true);
///
- /// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper Json data on the IPublishedContent item.
+ /// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper JSON data on the IPublishedContent item.
///
- ///
- /// The IPublishedContent item.
- ///
- ///
- /// The property alias of the property containing the Json data e.g. umbracoFile
- ///
- ///
- /// The crop alias e.g. thumbnail
- ///
- /// The image url generator.
+ /// The IPublishedContent item.
+ /// The property alias of the property containing the JSON data e.g. umbracoFile.
+ /// The crop alias e.g. thumbnail.
+ /// The image URL generator.
/// The published value fallback.
- /// The published url provider.
+ /// The published URL provider.
///
/// The URL of the cropped image.
///
@@ -92,74 +75,36 @@ public static string GetCropUrl(
string cropAlias,
IImageUrlGenerator imageUrlGenerator,
IPublishedValueFallback publishedValueFallback,
- IPublishedUrlProvider publishedUrlProvider)
- {
- return mediaItem.GetCropUrl( imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true);
- }
+ IPublishedUrlProvider publishedUrlProvider) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true);
public static string GetCropUrl(this MediaWithCrops mediaWithCrops,
IPublishedValueFallback publishedValueFallback,
IPublishedUrlProvider publishedUrlProvider,
string propertyAlias,
string cropAlias,
- IImageUrlGenerator imageUrlGenerator)
- {
- return mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true);
- }
+ IImageUrlGenerator imageUrlGenerator) => mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true);
///
/// Gets the underlying image processing service URL from the IPublishedContent item.
///
- ///
- /// The IPublishedContent item.
- ///
- /// The image url generator.
+ /// The IPublishedContent item.
+ /// The image URL generator.
/// The published value fallback.
- /// The published url provider.
- ///
- /// The width of the output image.
- ///
- ///
- /// The height of the output image.
- ///
- ///
- /// Property alias of the property containing the Json data.
- ///
- ///
- /// The crop alias.
- ///
- ///
- /// Quality percentage of the output image.
- ///
- ///
- /// The image crop mode.
- ///
- ///
- /// The image crop anchor.
- ///
- ///
- /// Use focal point, to generate an output image using the focal point instead of the predefined crop
- ///
- ///
- /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.
- ///
- ///
- /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated
- ///
- ///
- /// These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example:
- ///
- ///
- ///
- ///
- ///
- /// Use a dimension as a ratio
- ///
- ///
- /// If the image should be upscaled to requested dimensions
- ///
+ /// The published URL provider.
+ /// The width of the output image.
+ /// The height of the output image.
+ /// Property alias of the property containing the JSON data.
+ /// The crop alias.
+ /// Quality percentage of the output image.
+ /// The image crop mode.
+ /// The image crop anchor.
+ /// Use focal point, to generate an output image using the focal point instead of the predefined crop.
+ /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.
+ /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated.
+ /// These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example:
+ ///
///
/// The URL of the cropped image.
///
@@ -178,12 +123,7 @@ public static string GetCropUrl(
bool preferFocalPoint = false,
bool useCropDimensions = false,
bool cacheBuster = true,
- string furtherOptions = null,
- ImageCropRatioMode? ratioMode = null,
- bool upScale = true)
- {
- return mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, null, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode, upScale);
- }
+ string furtherOptions = null) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, null, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions);
public static string GetCropUrl(
this MediaWithCrops mediaWithCrops,
@@ -200,13 +140,14 @@ public static string GetCropUrl(
bool preferFocalPoint = false,
bool useCropDimensions = false,
bool cacheBuster = true,
- string furtherOptions = null,
- ImageCropRatioMode? ratioMode = null,
- bool upScale = true)
+ string furtherOptions = null)
{
- if (mediaWithCrops == null) throw new ArgumentNullException(nameof(mediaWithCrops));
+ if (mediaWithCrops == null)
+ {
+ throw new ArgumentNullException(nameof(mediaWithCrops));
+ }
- return mediaWithCrops.Content.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, mediaWithCrops.LocalCrops, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode, upScale);
+ return mediaWithCrops.Content.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, mediaWithCrops.LocalCrops, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions);
}
private static string GetCropUrl(
@@ -226,16 +167,17 @@ private static string GetCropUrl(
bool preferFocalPoint = false,
bool useCropDimensions = false,
bool cacheBuster = true,
- string furtherOptions = null,
- ImageCropRatioMode? ratioMode = null,
- bool upScale = true)
+ string furtherOptions = null)
{
- if (mediaItem == null) throw new ArgumentNullException(nameof(mediaItem));
-
- var cacheBusterValue = cacheBuster ? mediaItem.UpdateDate.ToFileTimeUtc().ToString(CultureInfo.InvariantCulture) : null;
+ if (mediaItem == null)
+ {
+ throw new ArgumentNullException(nameof(mediaItem));
+ }
if (mediaItem.HasProperty(propertyAlias) == false || mediaItem.HasValue(propertyAlias) == false)
- return string.Empty;
+ {
+ return null;
+ }
var mediaItemUrl = mediaItem.MediaUrl(publishedUrlProvider, propertyAlias: propertyAlias);
@@ -269,63 +211,34 @@ private static string GetCropUrl(
}
}
+ var cacheBusterValue = cacheBuster ? mediaItem.UpdateDate.ToFileTimeUtc().ToString(CultureInfo.InvariantCulture) : null;
+
return GetCropUrl(
mediaItemUrl, imageUrlGenerator, localCrops, width, height, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions,
- cacheBusterValue, furtherOptions, ratioMode, upScale);
+ cacheBusterValue, furtherOptions);
}
///
/// Gets the underlying image processing service URL from the image path.
///
- ///
- /// The image URL.
- ///
- ///
- /// The width of the output image.
- ///
- ///
- /// The height of the output image.
- ///
- ///
- /// The Json data from the Umbraco Core Image Cropper property editor
- ///
- ///
- /// The crop alias.
- ///
- ///
- /// Quality percentage of the output image.
- ///
- ///
- /// The image crop mode.
- ///
- ///
- /// The image crop anchor.
- ///
- ///
- /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one
- ///
- ///
- /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters
- ///
- ///
- /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated
- ///
- ///
- /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
- ///
- ///
- ///
- ///
- ///
- /// Use a dimension as a ratio
- ///
- ///
- /// If the image should be upscaled to requested dimensions
- ///
+ /// The image URL.
+ /// The image URL generator.
+ /// The width of the output image.
+ /// The height of the output image.
+ /// The Json data from the Umbraco Core Image Cropper property editor.
+ /// The crop alias.
+ /// Quality percentage of the output image.
+ /// The image crop mode.
+ /// The image crop anchor.
+ /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one.
+ /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.
+ /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated.
+ /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
+ ///
///
- /// The the URL of the cropped image.
+ /// The URL of the cropped image.
///
public static string GetCropUrl(
this string imageUrl,
@@ -340,11 +253,12 @@ public static string GetCropUrl(
bool preferFocalPoint = false,
bool useCropDimensions = false,
string cacheBusterValue = null,
- string furtherOptions = null,
- ImageCropRatioMode? ratioMode = null,
- bool upScale = true)
+ string furtherOptions = null)
{
- if (string.IsNullOrEmpty(imageUrl)) return string.Empty;
+ if (string.IsNullOrWhiteSpace(imageUrl))
+ {
+ return null;
+ }
ImageCropperValue cropDataSet = null;
if (string.IsNullOrEmpty(imageCropperValue) == false && imageCropperValue.DetectIsJson() && (imageCropMode == ImageCropMode.Crop || imageCropMode == null))
@@ -354,62 +268,30 @@ public static string GetCropUrl(
return GetCropUrl(
imageUrl, imageUrlGenerator, cropDataSet, width, height, cropAlias, quality, imageCropMode,
- imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, upScale);
+ imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions);
}
///
/// Gets the underlying image processing service URL from the image path.
///
- ///
- /// The image URL.
- ///
- ///
- /// The generator that will process all the options and the image URL to return a full image URLs with all processing options appended
- ///
- ///
- ///
- /// The width of the output image.
- ///
- ///
- /// The height of the output image.
- ///
- ///
- /// The crop alias.
- ///
- ///
- /// Quality percentage of the output image.
- ///
- ///
- /// The image crop mode.
- ///
- ///
- /// The image crop anchor.
- ///
- ///
- /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one
- ///
- ///
- /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters
- ///
- ///
- /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated
- ///
- ///
- /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
- ///
- ///
- ///
- ///
- ///
- /// Use a dimension as a ratio
- ///
- ///
- /// If the image should be upscaled to requested dimensions
- ///
+ /// The image URL.
+ /// The generator that will process all the options and the image URL to return a full image URLs with all processing options appended.
+ /// The crop data set.
+ /// The width of the output image.
+ /// The height of the output image.
+ /// The crop alias.
+ /// Quality percentage of the output image.
+ /// The image crop mode.
+ /// The image crop anchor.
+ /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one.
+ /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.
+ /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated.
+ /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
+ ///
///
- /// The .
+ /// The URL of the cropped image.
///
public static string GetCropUrl(
this string imageUrl,
@@ -424,72 +306,57 @@ public static string GetCropUrl(
bool preferFocalPoint = false,
bool useCropDimensions = false,
string cacheBusterValue = null,
- string furtherOptions = null,
- ImageCropRatioMode? ratioMode = null,
- bool upScale = true,
- string animationProcessMode = null)
+ string furtherOptions = null)
{
- if (string.IsNullOrEmpty(imageUrl)) return string.Empty;
+ if (string.IsNullOrWhiteSpace(imageUrl))
+ {
+ return null;
+ }
ImageUrlGenerationOptions options;
-
if (cropDataSet != null && (imageCropMode == ImageCropMode.Crop || imageCropMode == null))
{
- var crop = cropDataSet.GetCrop(cropAlias);
+ ImageCropperValue.ImageCropperCrop crop = cropDataSet.GetCrop(cropAlias);
- // if a crop was specified, but not found, return null
+ // If a crop was specified, but not found, return null
if (crop == null && !string.IsNullOrWhiteSpace(cropAlias))
+ {
return null;
+ }
- options = cropDataSet.GetCropBaseOptions(imageUrl, crop, string.IsNullOrWhiteSpace(cropAlias), preferFocalPoint);
+ options = cropDataSet.GetCropBaseOptions(imageUrl, crop, preferFocalPoint || string.IsNullOrWhiteSpace(cropAlias));
- if (crop != null & useCropDimensions)
+ if (crop != null && useCropDimensions)
{
width = crop.Width;
height = crop.Height;
}
- // If a predefined crop has been specified & there are no coordinates & no ratio mode, but a width parameter has been passed we can get the crop ratio for the height
- if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null && ratioMode == null && width != null && height == null)
- {
- options.HeightRatio = (decimal)crop.Height / crop.Width;
- }
-
- // If a predefined crop has been specified & there are no coordinates & no ratio mode, but a height parameter has been passed we can get the crop ratio for the width
- if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null && ratioMode == null && width == null && height != null)
+ // Calculate missing dimension if a predefined crop has been specified, but has no coordinates
+ if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null)
{
- options.WidthRatio = (decimal)crop.Width / crop.Height;
+ if (width != null && height == null)
+ {
+ height = (int)MathF.Round(width.Value * ((float)crop.Height / crop.Width));
+ }
+ else if (width == null && height != null)
+ {
+ width = (int)MathF.Round(height.Value * ((float)crop.Width / crop.Height));
+ }
}
}
else
{
- options = new ImageUrlGenerationOptions (imageUrl)
+ options = new ImageUrlGenerationOptions(imageUrl)
{
- ImageCropMode = (imageCropMode ?? ImageCropMode.Pad),
+ ImageCropMode = (imageCropMode ?? ImageCropMode.Pad), // Not sure why we default to Pad
ImageCropAnchor = imageCropAnchor
};
}
options.Quality = quality;
- options.Width = ratioMode != null && ratioMode.Value == ImageCropRatioMode.Width ? null : width;
- options.Height = ratioMode != null && ratioMode.Value == ImageCropRatioMode.Height ? null : height;
- options.AnimationProcessMode = animationProcessMode;
-
- if (ratioMode == ImageCropRatioMode.Width && height != null)
- {
- // if only height specified then assume a square
- if (width == null) width = height;
- options.WidthRatio = (decimal)width / (decimal)height;
- }
-
- if (ratioMode == ImageCropRatioMode.Height && width != null)
- {
- // if only width specified then assume a square
- if (height == null) height = width;
- options.HeightRatio = (decimal)height / (decimal)width;
- }
-
- options.UpScale = upScale;
+ options.Width = width;
+ options.Height = height;
options.FurtherOptions = furtherOptions;
options.CacheBusterValue = cacheBusterValue;
diff --git a/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs b/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs
index e7dd5248e19e..aefd1027256c 100644
--- a/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs
+++ b/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -246,8 +246,6 @@ public static IHtmlContent GetCropUrl(this IUrlHelper urlHelper,
bool useCropDimensions = false,
bool cacheBuster = true,
string furtherOptions = null,
- ImageCropRatioMode? ratioMode = null,
- bool upScale = true,
bool htmlEncode = true)
{
if (mediaItem == null)
@@ -256,8 +254,8 @@ public static IHtmlContent GetCropUrl(this IUrlHelper urlHelper,
}
var url = mediaItem.GetCropUrl(width, height, propertyAlias, cropAlias, quality, imageCropMode,
- imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode,
- upScale);
+ imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions);
+
return CreateHtmlString(url, htmlEncode);
}
@@ -273,16 +271,14 @@ public static IHtmlContent GetCropUrl(this IUrlHelper urlHelper,
bool useCropDimensions = true,
string cacheBusterValue = null,
string furtherOptions = null,
- ImageCropRatioMode? ratioMode = null,
- bool upScale = true,
bool htmlEncode = true)
{
if (imageCropperValue == null) return HtmlString.Empty;
var imageUrl = imageCropperValue.Src;
var url = imageUrl.GetCropUrl(imageCropperValue, width, height, cropAlias, quality, imageCropMode,
- imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode,
- upScale);
+ imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions);
+
return CreateHtmlString(url, htmlEncode);
}
diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs
new file mode 100644
index 000000000000..7b3cc817f232
--- /dev/null
+++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs
@@ -0,0 +1,64 @@
+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;
+
+namespace Umbraco.Cms.Web.Common.ImageProcessors
+{
+ ///
+ /// Allows the cropping of images.
+ ///
+ public class CropWebProcessor : IImageWebProcessor
+ {
+ ///
+ /// The command constant for the crop coordinates.
+ ///
+ public const string Coordinates = "cc";
+
+ ///
+ public IEnumerable Commands { get; } = new[]
+ {
+ Coordinates
+ };
+
+ ///
+ public FormattedImage Process(FormattedImage image, ILogger logger, IDictionary commands, CommandParser parser, CultureInfo culture)
+ {
+ RectangleF? coordinates = GetCoordinates(commands, parser, culture);
+ if (coordinates != null)
+ {
+ // 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));
+ }
+
+ return image;
+ }
+
+ private static RectangleF? GetCoordinates(IDictionary commands, CommandParser parser, CultureInfo culture)
+ {
+ float[] coordinates = parser.ParseValue(commands.GetValueOrDefault(Coordinates), culture);
+
+ if (coordinates.Length != 4)
+ {
+ 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]);
+ }
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/imageurlgenerator.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/imageurlgenerator.resource.js
index a937cd2675a9..dd65f89526d5 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/imageurlgenerator.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/imageurlgenerator.resource.js
@@ -1,4 +1,4 @@
-/**
+/**
* @ngdoc service
* @name umbraco.resources.imageUrlGeneratorResource
* @function
@@ -11,14 +11,14 @@
function imageUrlGeneratorResource($http, umbRequestHelper) {
- function getCropUrl(mediaPath, width, height, imageCropMode, animationProcessMode) {
+ function getCropUrl(mediaPath, width, height, imageCropMode) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"imageUrlGeneratorApiBaseUrl",
"GetCropUrl",
- { mediaPath, width, height, imageCropMode, animationProcessMode })),
+ { mediaPath, width, height, imageCropMode })),
'Failed to get crop URL');
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js
index 1b3765a5f514..e98a597e764b 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js
@@ -1,4 +1,4 @@
-/**
+/**
* @ngdoc service
* @name umbraco.services.mediaHelper
* @description A helper object used for dealing with media items
@@ -408,16 +408,20 @@ function mediaHelper(umbRequestHelper, $http, $log) {
* @param {string} imagePath Raw image path
* @param {object} options Object describing image generation parameters:
* {
- * animationProcessMode:
- * cacheBusterValue:
+ * width:
+ * height:
* focalPoint: {
* left:
* top:
* },
- * height:
* mode:
- * upscale:
- * width:
+ * cacheBusterValue:
+ * crop: {
+ * x1:
+ * x2:
+ * y1:
+ * y2:
+ * },
* }
*/
getProcessedImageUrl: function (imagePath, options) {
@@ -433,18 +437,16 @@ function mediaHelper(umbRequestHelper, $http, $log) {
"GetProcessedImageUrl",
{
imagePath,
- animationProcessMode: options.animationProcessMode,
- cacheBusterValue: options.cacheBusterValue,
+ width: options.width,
+ height: options.height,
focalPointLeft: options.focalPoint ? options.focalPoint.left : null,
focalPointTop: options.focalPoint ? options.focalPoint.top : null,
- height: options.height,
mode: options.mode,
- upscale: options.upscale || false,
- width: options.width,
+ cacheBusterValue: options.cacheBusterValue,
cropX1: options.crop ? options.crop.x1 : null,
cropX2: options.crop ? options.crop.x2 : null,
cropY1: options.crop ? options.crop.y1 : null,
- cropY2: options.crop ? options.crop.y : null
+ cropY2: options.crop ? options.crop.y2 : null
})),
"Failed to retrieve processed image URL for image: " + imagePath);
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
index b83367ef6e97..2738883b151f 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
@@ -306,8 +306,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
if (imgUrl) {
mediaHelper.getProcessedImageUrl(imgUrl,
{
- height: newSize.height,
- width: newSize.width
+ width: newSize.width,
+ height: newSize.height
})
.then(function (resizedImgUrl) {
editor.dom.setAttrib(imageDomElement, 'data-mce-src', resizedImgUrl);
@@ -1522,15 +1522,13 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
args.editor.on('ObjectResized', function (e) {
var srcAttr = $(e.target).attr("src");
var path = srcAttr.split("?")[0];
- mediaHelper.getProcessedImageUrl(path,
- {
- height: e.height,
- moded: "max",
- width: e.width
- })
- .then(function (resizedPath) {
- $(e.target).attr("data-mce-src", resizedPath);
- });
+ mediaHelper.getProcessedImageUrl(path, {
+ width: e.width,
+ height: e.height,
+ mode: "max"
+ }).then(function (resizedPath) {
+ $(e.target).attr("data-mce-src", resizedPath);
+ });
syncContent();
});
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umbBlockCard.component.js b/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umbBlockCard.component.js
index 0c75bfbee311..a9993a049848 100644
--- a/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umbBlockCard.component.js
+++ b/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umbBlockCard.component.js
@@ -42,7 +42,7 @@
var path = umbRequestHelper.convertVirtualToAbsolutePath(vm.blockConfigModel.thumbnail);
if (path.toLowerCase().endsWith(".svg") === false) {
- path += "?upscale=false&width=400";
+ path += "?width=400";
}
vm.styleBackgroundImage = 'url(\''+path+'\')';
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js
index 4f1016e68028..b4d59c683c73 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js
@@ -1,4 +1,4 @@
-(function () {
+(function () {
'use strict';
/**
@@ -55,7 +55,7 @@
if (thumbnail) {
if (mediaHelper.detectIfImageByExtension(property.value)) {
//get default big thumbnail from image processor
- var thumbnailUrl = property.value + "?rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss") + "&width=500&animationprocessmode=first";
+ var thumbnailUrl = property.value + "?width=500&rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss");
return thumbnailUrl;
}
else {
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js
index 91da54d4ad26..81a548a1169a 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js
@@ -70,28 +70,25 @@ angular.module("umbraco")
if ($scope.control.value.coordinates) {
// New way, crop by percent must come before width/height.
var coords = $scope.control.value.coordinates;
- url += `?crop=${coords.x1},${coords.y1},${coords.x2},${coords.y2}&cropmode=percentage`;
+ url += `?cc=${coords.x1},${coords.y1},${coords.x2},${coords.y2}`;
} else {
// Here in order not to break existing content where focalPoint were used.
- // For some reason width/height have to come first when mode=crop.
if ($scope.control.value.focalPoint) {
- url += `?center=${$scope.control.value.focalPoint.top},${$scope.control.value.focalPoint.left}`;
- url += '&mode=crop';
+ url += `?rxy=${$scope.control.value.focalPoint.left},${$scope.control.value.focalPoint.top}`;
} else {
// Prevent black padding and no crop when focal point not set / changed from default
- url += '?center=0.5,0.5&mode=crop';
+ url += '?rxy=0.5,0.5';
}
}
url += '&width=' + $scope.control.editor.config.size.width;
url += '&height=' + $scope.control.editor.config.size.height;
- url += '&animationprocessmode=first';
}
// set default size if no crop present (moved from the view)
if (url.includes('?') === false)
{
- url += '?width=800&upscale=false&animationprocessmode=false'
+ url += '?width=800'
}
return url;
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js
index cbaf843d3554..8bb50a07dc01 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js
@@ -234,7 +234,7 @@ angular.module('umbraco')
if (property.value && property.value.src) {
if (thumbnail === true) {
- return property.value.src + "?width=500&mode=max&animationprocessmode=first";
+ return property.value.src + "?width=500";
}
else {
return property.value.src;