diff --git a/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs b/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs
index 81b1c2c663..f494ed9094 100644
--- a/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Processors.Transforms;
@@ -12,16 +12,6 @@ namespace SixLabors.ImageSharp.Processing
///
public static class ResizeExtensions
{
- ///
- /// Resizes an image in accordance with the given .
- ///
- /// The image to resize.
- /// The resize options.
- /// The to allow chaining of operations.
- /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image or the nearest possible ratio.
- public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options)
- => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize()));
-
///
/// Resizes an image to the given .
///
@@ -128,7 +118,18 @@ public static IImageProcessingContext Resize(
Rectangle sourceRectangle,
Rectangle targetRectangle,
bool compand)
- => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand), sourceRectangle);
+ {
+ var options = new ResizeOptions
+ {
+ Size = new Size(width, height),
+ Mode = ResizeMode.Manual,
+ Sampler = sampler,
+ TargetRectangle = targetRectangle,
+ Compand = compand
+ };
+
+ return source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize()), sourceRectangle);
+ }
///
/// Resizes an image to the given width and height with the given sampler and source rectangle.
@@ -150,6 +151,27 @@ public static IImageProcessingContext Resize(
IResampler sampler,
Rectangle targetRectangle,
bool compand)
- => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand));
+ {
+ var options = new ResizeOptions
+ {
+ Size = new Size(width, height),
+ Mode = ResizeMode.Manual,
+ Sampler = sampler,
+ TargetRectangle = targetRectangle,
+ Compand = compand
+ };
+
+ return Resize(source, options);
+ }
+
+ ///
+ /// Resizes an image in accordance with the given .
+ ///
+ /// The image to resize.
+ /// The resize options.
+ /// The to allow chaining of operations.
+ /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image or the nearest possible ratio.
+ public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options)
+ => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize()));
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs
index ae7b112fc1..eacd3834f1 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs
@@ -2,9 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Linq;
using System.Numerics;
-
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@@ -30,17 +28,34 @@ public static unsafe int CalculateResizeWorkerHeightInWindowBands(
///
/// The source image size.
/// The resize options.
- /// The target width
- /// The target height
///
/// The tuple representing the location and the bounds
///
- public static (Size, Rectangle) CalculateTargetLocationAndBounds(
- Size sourceSize,
- ResizeOptions options,
- int width,
- int height)
+ public static (Size, Rectangle) CalculateTargetLocationAndBounds(Size sourceSize, ResizeOptions options)
{
+ int width = options.Size.Width;
+ int height = options.Size.Height;
+
+ if (width <= 0 && height <= 0)
+ {
+ ThrowInvalid($"Target width {width} and height {height} must be greater than zero.");
+ }
+
+ // Ensure target size is populated across both dimensions.
+ // These dimensions are used to calculate the final dimensions determined by the mode algorithm.
+ // If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio.
+ // If it is not possible to keep aspect ratio, make sure at least the minimum is is kept.
+ const int Min = 1;
+ if (width == 0 && height > 0)
+ {
+ width = (int)MathF.Max(Min, MathF.Round(sourceSize.Width * height / (float)sourceSize.Height));
+ }
+
+ if (height == 0 && width > 0)
+ {
+ height = (int)MathF.Max(Min, MathF.Round(sourceSize.Height * width / (float)sourceSize.Width));
+ }
+
switch (options.Mode)
{
case ResizeMode.Crop:
@@ -50,11 +65,13 @@ public static (Size, Rectangle) CalculateTargetLocationAndBounds(
case ResizeMode.BoxPad:
return CalculateBoxPadRectangle(sourceSize, options, width, height);
case ResizeMode.Max:
- return CalculateMaxRectangle(sourceSize, options, width, height);
+ return CalculateMaxRectangle(sourceSize, width, height);
case ResizeMode.Min:
- return CalculateMinRectangle(sourceSize, options, width, height);
+ return CalculateMinRectangle(sourceSize, width, height);
+ case ResizeMode.Manual:
+ return CalculateManualRectangle(options, width, height);
- // Last case ResizeMode.Stretch:
+ // case ResizeMode.Stretch:
default:
return (new Size(width, height), new Rectangle(0, 0, width, height));
}
@@ -66,11 +83,6 @@ private static (Size, Rectangle) CalculateBoxPadRectangle(
int width,
int height)
{
- if (width <= 0 || height <= 0)
- {
- return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height));
- }
-
int sourceWidth = source.Width;
int sourceHeight = source.Height;
@@ -84,55 +96,55 @@ private static (Size, Rectangle) CalculateBoxPadRectangle(
// Only calculate if upscaling.
if (sourceWidth < boxPadWidth && sourceHeight < boxPadHeight)
{
- int destinationX;
- int destinationY;
- int destinationWidth = sourceWidth;
- int destinationHeight = sourceHeight;
+ int targetX;
+ int targetY;
+ int targetWidth = sourceWidth;
+ int targetHeight = sourceHeight;
width = boxPadWidth;
height = boxPadHeight;
switch (options.Position)
{
case AnchorPositionMode.Left:
- destinationY = (height - sourceHeight) / 2;
- destinationX = 0;
+ targetY = (height - sourceHeight) / 2;
+ targetX = 0;
break;
case AnchorPositionMode.Right:
- destinationY = (height - sourceHeight) / 2;
- destinationX = width - sourceWidth;
+ targetY = (height - sourceHeight) / 2;
+ targetX = width - sourceWidth;
break;
case AnchorPositionMode.TopRight:
- destinationY = 0;
- destinationX = width - sourceWidth;
+ targetY = 0;
+ targetX = width - sourceWidth;
break;
case AnchorPositionMode.Top:
- destinationY = 0;
- destinationX = (width - sourceWidth) / 2;
+ targetY = 0;
+ targetX = (width - sourceWidth) / 2;
break;
case AnchorPositionMode.TopLeft:
- destinationY = 0;
- destinationX = 0;
+ targetY = 0;
+ targetX = 0;
break;
case AnchorPositionMode.BottomRight:
- destinationY = height - sourceHeight;
- destinationX = width - sourceWidth;
+ targetY = height - sourceHeight;
+ targetX = width - sourceWidth;
break;
case AnchorPositionMode.Bottom:
- destinationY = height - sourceHeight;
- destinationX = (width - sourceWidth) / 2;
+ targetY = height - sourceHeight;
+ targetX = (width - sourceWidth) / 2;
break;
case AnchorPositionMode.BottomLeft:
- destinationY = height - sourceHeight;
- destinationX = 0;
+ targetY = height - sourceHeight;
+ targetX = 0;
break;
default:
- destinationY = (height - sourceHeight) / 2;
- destinationX = (width - sourceWidth) / 2;
+ targetY = (height - sourceHeight) / 2;
+ targetX = (width - sourceWidth) / 2;
break;
}
- return (new Size(width, height),
- new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
+ // Target image width and height can be different to the rectangle width and height.
+ return (new Size(width, height), new Rectangle(targetX, targetY, targetWidth, targetHeight));
}
// Switch to pad mode to downscale and calculate from there.
@@ -145,19 +157,14 @@ private static (Size, Rectangle) CalculateCropRectangle(
int width,
int height)
{
- if (width <= 0 || height <= 0)
- {
- return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height));
- }
-
float ratio;
int sourceWidth = source.Width;
int sourceHeight = source.Height;
- int destinationX = 0;
- int destinationY = 0;
- int destinationWidth = width;
- int destinationHeight = height;
+ int targetX = 0;
+ int targetY = 0;
+ int targetWidth = width;
+ int targetHeight = height;
// Fractional variants for preserving aspect ratio.
float percentHeight = MathF.Abs(height / (float)sourceHeight);
@@ -167,19 +174,19 @@ private static (Size, Rectangle) CalculateCropRectangle(
{
ratio = percentWidth;
- if (options.CenterCoordinates.Any())
+ if (options.CenterCoordinates.HasValue)
{
- float center = -(ratio * sourceHeight) * options.CenterCoordinates.ToArray()[1];
- destinationY = (int)MathF.Round(center + (height / 2F));
+ float center = -(ratio * sourceHeight) * options.CenterCoordinates.Value.Y;
+ targetY = (int)MathF.Round(center + (height / 2F));
- if (destinationY > 0)
+ if (targetY > 0)
{
- destinationY = 0;
+ targetY = 0;
}
- if (destinationY < (int)MathF.Round(height - (sourceHeight * ratio)))
+ if (targetY < (int)MathF.Round(height - (sourceHeight * ratio)))
{
- destinationY = (int)MathF.Round(height - (sourceHeight * ratio));
+ targetY = (int)MathF.Round(height - (sourceHeight * ratio));
}
}
else
@@ -189,38 +196,38 @@ private static (Size, Rectangle) CalculateCropRectangle(
case AnchorPositionMode.Top:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.TopRight:
- destinationY = 0;
+ targetY = 0;
break;
case AnchorPositionMode.Bottom:
case AnchorPositionMode.BottomLeft:
case AnchorPositionMode.BottomRight:
- destinationY = (int)MathF.Round(height - (sourceHeight * ratio));
+ targetY = (int)MathF.Round(height - (sourceHeight * ratio));
break;
default:
- destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F);
+ targetY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F);
break;
}
}
- destinationHeight = (int)MathF.Ceiling(sourceHeight * percentWidth);
+ targetHeight = (int)MathF.Ceiling(sourceHeight * percentWidth);
}
else
{
ratio = percentHeight;
- if (options.CenterCoordinates.Any())
+ if (options.CenterCoordinates.HasValue)
{
- float center = -(ratio * sourceWidth) * options.CenterCoordinates.First();
- destinationX = (int)MathF.Round(center + (width / 2F));
+ float center = -(ratio * sourceWidth) * options.CenterCoordinates.Value.X;
+ targetX = (int)MathF.Round(center + (width / 2F));
- if (destinationX > 0)
+ if (targetX > 0)
{
- destinationX = 0;
+ targetX = 0;
}
- if (destinationX < (int)MathF.Round(width - (sourceWidth * ratio)))
+ if (targetX < (int)MathF.Round(width - (sourceWidth * ratio)))
{
- destinationX = (int)MathF.Round(width - (sourceWidth * ratio));
+ targetX = (int)MathF.Round(width - (sourceWidth * ratio));
}
}
else
@@ -230,68 +237,64 @@ private static (Size, Rectangle) CalculateCropRectangle(
case AnchorPositionMode.Left:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.BottomLeft:
- destinationX = 0;
+ targetX = 0;
break;
case AnchorPositionMode.Right:
case AnchorPositionMode.TopRight:
case AnchorPositionMode.BottomRight:
- destinationX = (int)MathF.Round(width - (sourceWidth * ratio));
+ targetX = (int)MathF.Round(width - (sourceWidth * ratio));
break;
default:
- destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F);
+ targetX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F);
break;
}
}
- destinationWidth = (int)MathF.Ceiling(sourceWidth * percentHeight);
+ targetWidth = (int)MathF.Ceiling(sourceWidth * percentHeight);
}
- return (new Size(width, height),
- new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
+ // Target image width and height can be different to the rectangle width and height.
+ return (new Size(width, height), new Rectangle(targetX, targetY, targetWidth, targetHeight));
}
private static (Size, Rectangle) CalculateMaxRectangle(
Size source,
- ResizeOptions options,
int width,
int height)
{
- int destinationWidth = width;
- int destinationHeight = height;
+ int targetWidth = width;
+ int targetHeight = height;
// Fractional variants for preserving aspect ratio.
float percentHeight = MathF.Abs(height / (float)source.Height);
float percentWidth = MathF.Abs(width / (float)source.Width);
// Integers must be cast to floats to get needed precision
- float ratio = options.Size.Height / (float)options.Size.Width;
+ float ratio = height / (float)width;
float sourceRatio = source.Height / (float)source.Width;
if (sourceRatio < ratio)
{
- destinationHeight = (int)MathF.Round(source.Height * percentWidth);
- height = destinationHeight;
+ targetHeight = (int)MathF.Round(source.Height * percentWidth);
}
else
{
- destinationWidth = (int)MathF.Round(source.Width * percentHeight);
- width = destinationWidth;
+ targetWidth = (int)MathF.Round(source.Width * percentHeight);
}
// Replace the size to match the rectangle.
- return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight));
+ return (new Size(targetWidth, targetHeight), new Rectangle(0, 0, targetWidth, targetHeight));
}
private static (Size, Rectangle) CalculateMinRectangle(
Size source,
- ResizeOptions options,
int width,
int height)
{
int sourceWidth = source.Width;
int sourceHeight = source.Height;
- int destinationWidth;
- int destinationHeight;
+ int targetWidth = width;
+ int targetHeight = height;
// Don't upscale
if (width > sourceWidth || height > sourceHeight)
@@ -306,58 +309,45 @@ private static (Size, Rectangle) CalculateMinRectangle(
if (widthDiff < heightDiff)
{
float sourceRatio = (float)sourceHeight / sourceWidth;
- destinationHeight = (int)MathF.Round(width * sourceRatio);
- height = destinationHeight;
- destinationWidth = width;
+ targetHeight = (int)MathF.Round(width * sourceRatio);
}
else if (widthDiff > heightDiff)
{
float sourceRatioInverse = (float)sourceWidth / sourceHeight;
- destinationWidth = (int)MathF.Round(height * sourceRatioInverse);
- destinationHeight = height;
- width = destinationWidth;
+ targetWidth = (int)MathF.Round(height * sourceRatioInverse);
}
else
{
if (height > width)
{
- destinationWidth = width;
float percentWidth = MathF.Abs(width / (float)sourceWidth);
- destinationHeight = (int)MathF.Round(sourceHeight * percentWidth);
- height = destinationHeight;
+ targetHeight = (int)MathF.Round(sourceHeight * percentWidth);
}
else
{
- destinationHeight = height;
float percentHeight = MathF.Abs(height / (float)sourceHeight);
- destinationWidth = (int)MathF.Round(sourceWidth * percentHeight);
- width = destinationWidth;
+ targetWidth = (int)MathF.Round(sourceWidth * percentHeight);
}
}
// Replace the size to match the rectangle.
- return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight));
+ return (new Size(targetWidth, targetHeight), new Rectangle(0, 0, targetWidth, targetHeight));
}
private static (Size, Rectangle) CalculatePadRectangle(
- Size source,
+ Size sourceSize,
ResizeOptions options,
int width,
int height)
{
- if (width <= 0 || height <= 0)
- {
- return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height));
- }
-
float ratio;
- int sourceWidth = source.Width;
- int sourceHeight = source.Height;
+ int sourceWidth = sourceSize.Width;
+ int sourceHeight = sourceSize.Height;
- int destinationX = 0;
- int destinationY = 0;
- int destinationWidth = width;
- int destinationHeight = height;
+ int targetX = 0;
+ int targetY = 0;
+ int targetWidth = width;
+ int targetHeight = height;
// Fractional variants for preserving aspect ratio.
float percentHeight = MathF.Abs(height / (float)sourceHeight);
@@ -366,50 +356,73 @@ private static (Size, Rectangle) CalculatePadRectangle(
if (percentHeight < percentWidth)
{
ratio = percentHeight;
- destinationWidth = (int)MathF.Round(sourceWidth * percentHeight);
+ targetWidth = (int)MathF.Round(sourceWidth * percentHeight);
switch (options.Position)
{
case AnchorPositionMode.Left:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.BottomLeft:
- destinationX = 0;
+ targetX = 0;
break;
case AnchorPositionMode.Right:
case AnchorPositionMode.TopRight:
case AnchorPositionMode.BottomRight:
- destinationX = (int)MathF.Round(width - (sourceWidth * ratio));
+ targetX = (int)MathF.Round(width - (sourceWidth * ratio));
break;
default:
- destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F);
+ targetX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F);
break;
}
}
else
{
ratio = percentWidth;
- destinationHeight = (int)MathF.Round(sourceHeight * percentWidth);
+ targetHeight = (int)MathF.Round(sourceHeight * percentWidth);
switch (options.Position)
{
case AnchorPositionMode.Top:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.TopRight:
- destinationY = 0;
+ targetY = 0;
break;
case AnchorPositionMode.Bottom:
case AnchorPositionMode.BottomLeft:
case AnchorPositionMode.BottomRight:
- destinationY = (int)MathF.Round(height - (sourceHeight * ratio));
+ targetY = (int)MathF.Round(height - (sourceHeight * ratio));
break;
default:
- destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F);
+ targetY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F);
break;
}
}
- return (new Size(width, height),
- new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
+ // Target image width and height can be different to the rectangle width and height.
+ return (new Size(width, height), new Rectangle(targetX, targetY, targetWidth, targetHeight));
}
+
+ private static (Size, Rectangle) CalculateManualRectangle(
+ ResizeOptions options,
+ int width,
+ int height)
+ {
+ if (!options.TargetRectangle.HasValue)
+ {
+ ThrowInvalid("Manual resizing requires a target location and size.");
+ }
+
+ Rectangle targetRectangle = options.TargetRectangle.Value;
+
+ int targetX = targetRectangle.X;
+ int targetY = targetRectangle.Y;
+ int targetWidth = targetRectangle.Width > 0 ? targetRectangle.Width : width;
+ int targetHeight = targetRectangle.Height > 0 ? targetRectangle.Height : height;
+
+ // Target image width and height can be different to the rectangle width and height.
+ return (new Size(width, height), new Rectangle(targetX, targetY, targetWidth, targetHeight));
+ }
+
+ private static void ThrowInvalid(string message) => throw new InvalidOperationException(message);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
index cf27de5eb1..35e22757c1 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
@@ -13,45 +13,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
public class ResizeProcessor : IImageProcessor
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The .
- /// The width.
- /// The height.
- /// The size of the source image.
- /// The target rectangle to resize into.
- /// A value indicating whether to apply RGBA companding.
- public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize, Rectangle targetRectangle, bool compand)
- {
- Guard.NotNull(sampler, nameof(sampler));
-
- // Ensure size is populated across both dimensions.
- // If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio.
- // If it is not possible to keep aspect ratio, make sure at least the minimum is is kept.
- const int min = 1;
- if (width == 0 && height > 0)
- {
- width = (int)MathF.Max(min, MathF.Round(sourceSize.Width * height / (float)sourceSize.Height));
- targetRectangle.Width = width;
- }
-
- if (height == 0 && width > 0)
- {
- height = (int)MathF.Max(min, MathF.Round(sourceSize.Height * width / (float)sourceSize.Width));
- targetRectangle.Height = height;
- }
-
- Guard.MustBeGreaterThan(width, 0, nameof(width));
- Guard.MustBeGreaterThan(height, 0, nameof(height));
-
- this.Sampler = sampler;
- this.Width = width;
- this.Height = height;
- this.TargetRectangle = targetRectangle;
- this.Compand = compand;
- }
-
///
/// Initializes a new instance of the class.
///
@@ -62,48 +23,15 @@ public ResizeProcessor(ResizeOptions options, Size sourceSize)
Guard.NotNull(options, nameof(options));
Guard.NotNull(options.Sampler, nameof(options.Sampler));
- int targetWidth = options.Size.Width;
- int targetHeight = options.Size.Height;
-
- // Ensure size is populated across both dimensions.
- // These dimensions are used to calculate the final dimensions determined by the mode algorithm.
- // If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio.
- // If it is not possible to keep aspect ratio, make sure at least the minimum is is kept.
- const int min = 1;
- if (targetWidth == 0 && targetHeight > 0)
- {
- targetWidth = (int)MathF.Max(min, MathF.Round(sourceSize.Width * targetHeight / (float)sourceSize.Height));
- }
-
- if (targetHeight == 0 && targetWidth > 0)
- {
- targetHeight = (int)MathF.Max(min, MathF.Round(sourceSize.Height * targetWidth / (float)sourceSize.Width));
- }
-
- Guard.MustBeGreaterThan(targetWidth, 0, nameof(targetWidth));
- Guard.MustBeGreaterThan(targetHeight, 0, nameof(targetHeight));
-
- (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, targetWidth, targetHeight);
+ (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options);
this.Sampler = options.Sampler;
- this.Width = size.Width;
- this.Height = size.Height;
+ this.TargetWidth = size.Width;
+ this.TargetHeight = size.Height;
this.TargetRectangle = rectangle;
this.Compand = options.Compand;
}
- ///
- /// Initializes a new instance of the class.
- ///
- /// The sampler to perform the resize operation.
- /// The target width.
- /// The target height.
- /// The source image size
- public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize)
- : this(sampler, width, height, sourceSize, new Rectangle(0, 0, width, height), false)
- {
- }
-
///
/// Gets the sampler to perform the resize operation.
///
@@ -112,12 +40,12 @@ public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSiz
///
/// Gets the target width.
///
- public int Width { get; }
+ public int TargetWidth { get; }
///
/// Gets the target height.
///
- public int Height { get; }
+ public int TargetHeight { get; }
///
/// Gets the resize rectangle.
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
index e16d4801ec..b85983a481 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
@@ -45,15 +45,15 @@ public ResizeProcessor(ResizeProcessor parameterSource, Image source, Re
///
/// Gets the target width.
///
- public int Width => this.parameterSource.Width;
+ public int TargetWidth => this.parameterSource.TargetWidth;
///
/// Gets the target height.
///
- public int Height => this.parameterSource.Height;
+ public int TargetHeight => this.parameterSource.TargetHeight;
///
- /// Gets the resize rectangle.
+ /// Gets the target resize rectangle.
///
public Rectangle TargetRectangle => this.parameterSource.TargetRectangle;
@@ -80,8 +80,8 @@ protected override Image CreateDestination()
IEnumerable> frames = source.Frames.Select, ImageFrame>(
x => new ImageFrame(
configuration,
- this.Width,
- this.Height,
+ this.TargetWidth,
+ this.TargetHeight,
x.Metadata.DeepClone()));
// Use the overload to prevent an extra frame being added
@@ -128,8 +128,8 @@ protected override void OnFrameApply(ImageFrame source, ImageFrame
/// Stretches the resized image to fit the bounds of its container.
///
- Stretch
+ Stretch,
+
+ ///
+ /// The target location and size of the resized image has been manually set.
+ ///
+ Manual
}
}
diff --git a/src/ImageSharp/Processing/ResizeOptions.cs b/src/ImageSharp/Processing/ResizeOptions.cs
index ee0dde6b27..ef88dc35b3 100644
--- a/src/ImageSharp/Processing/ResizeOptions.cs
+++ b/src/ImageSharp/Processing/ResizeOptions.cs
@@ -1,8 +1,6 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
-using System.Collections.Generic;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Primitives;
@@ -26,7 +24,7 @@ public class ResizeOptions
///
/// Gets or sets the center coordinates.
///
- public IEnumerable CenterCoordinates { get; set; } = Array.Empty();
+ public PointF? CenterCoordinates { get; set; }
///
/// Gets or sets the target size.
@@ -43,5 +41,10 @@ public class ResizeOptions
/// or expand individual pixel colors the value on processing.
///
public bool Compand { get; set; } = false;
+
+ ///
+ /// Gets or sets the target rectangle to resize into.
+ ///
+ public Rectangle? TargetRectangle { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs
index b5ed64f7ef..b351ec235f 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs
@@ -11,19 +11,18 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
public class ResizeHelperTests
{
-
[Theory]
[InlineData(20, 100, 1, 2)]
- [InlineData(20, 100, 20*100*16, 2)]
- [InlineData(20, 100, 40*100*16, 2)]
- [InlineData(20, 100, 59*100*16, 2)]
- [InlineData(20, 100, 60*100*16, 3)]
- [InlineData(17, 63, 5*17*63*16, 5)]
- [InlineData(17, 63, 5*17*63*16+1, 5)]
- [InlineData(17, 63, 6*17*63*16-1, 5)]
- [InlineData(33, 400, 1*1024*1024, 4)]
- [InlineData(33, 400, 8*1024*1024, 39)]
- [InlineData(50, 300, 1*1024*1024, 4)]
+ [InlineData(20, 100, 20 * 100 * 16, 2)]
+ [InlineData(20, 100, 40 * 100 * 16, 2)]
+ [InlineData(20, 100, 59 * 100 * 16, 2)]
+ [InlineData(20, 100, 60 * 100 * 16, 3)]
+ [InlineData(17, 63, 5 * 17 * 63 * 16, 5)]
+ [InlineData(17, 63, (5 * 17 * 63 * 16) + 1, 5)]
+ [InlineData(17, 63, (6 * 17 * 63 * 16) - 1, 5)]
+ [InlineData(33, 400, 1 * 1024 * 1024, 4)]
+ [InlineData(33, 400, 8 * 1024 * 1024, 39)]
+ [InlineData(50, 300, 1 * 1024 * 1024, 4)]
public void CalculateResizeWorkerHeightInWindowBands(
int windowDiameter,
int width,
@@ -40,17 +39,121 @@ public void CalculateMinRectangleWhenSourceIsSmallerThanTarget()
var sourceSize = new Size(200, 100);
var target = new Size(400, 200);
- var actual = ResizeHelper.CalculateTargetLocationAndBounds(
+ (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(
sourceSize,
- new ResizeOptions{
+ new ResizeOptions
+ {
Mode = ResizeMode.Min,
Size = target
- },
- target.Width,
- target.Height);
-
- Assert.Equal(sourceSize, actual.Item1);
- Assert.Equal(new Rectangle(0, 0, sourceSize.Width, sourceSize.Height), actual.Item2);
+ });
+
+ Assert.Equal(sourceSize, size);
+ Assert.Equal(new Rectangle(0, 0, sourceSize.Width, sourceSize.Height), rectangle);
+ }
+
+ [Fact]
+ public void MaxSizeAndRectangleAreCorrect()
+ {
+ var sourceSize = new Size(5072, 6761);
+ var target = new Size(0, 450);
+
+ var expectedSize = new Size(338, 450);
+ var expectedRectangle = new Rectangle(Point.Empty, expectedSize);
+
+ (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(
+ sourceSize,
+ new ResizeOptions
+ {
+ Mode = ResizeMode.Max,
+ Size = target
+ });
+
+ Assert.Equal(expectedSize, size);
+ Assert.Equal(expectedRectangle, rectangle);
+ }
+
+ [Fact]
+ public void CropSizeAndRectangleAreCorrect()
+ {
+ var sourceSize = new Size(100, 100);
+ var target = new Size(25, 50);
+
+ var expectedSize = new Size(25, 50);
+ var expectedRectangle = new Rectangle(-12, 0, 50, 50);
+
+ (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(
+ sourceSize,
+ new ResizeOptions
+ {
+ Mode = ResizeMode.Crop,
+ Size = target
+ });
+
+ Assert.Equal(expectedSize, size);
+ Assert.Equal(expectedRectangle, rectangle);
+ }
+
+ [Fact]
+ public void BoxPadSizeAndRectangleAreCorrect()
+ {
+ var sourceSize = new Size(100, 100);
+ var target = new Size(120, 110);
+
+ var expectedSize = new Size(120, 110);
+ var expectedRectangle = new Rectangle(10, 5, 100, 100);
+
+ (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(
+ sourceSize,
+ new ResizeOptions
+ {
+ Mode = ResizeMode.BoxPad,
+ Size = target
+ });
+
+ Assert.Equal(expectedSize, size);
+ Assert.Equal(expectedRectangle, rectangle);
+ }
+
+ [Fact]
+ public void PadSizeAndRectangleAreCorrect()
+ {
+ var sourceSize = new Size(100, 100);
+ var target = new Size(120, 110);
+
+ var expectedSize = new Size(120, 110);
+ var expectedRectangle = new Rectangle(5, 0, 110, 110);
+
+ (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(
+ sourceSize,
+ new ResizeOptions
+ {
+ Mode = ResizeMode.Pad,
+ Size = target
+ });
+
+ Assert.Equal(expectedSize, size);
+ Assert.Equal(expectedRectangle, rectangle);
+ }
+
+ [Fact]
+ public void StretchSizeAndRectangleAreCorrect()
+ {
+ var sourceSize = new Size(100, 100);
+ var target = new Size(57, 32);
+
+ var expectedSize = new Size(57, 32);
+ var expectedRectangle = new Rectangle(Point.Empty, expectedSize);
+
+ (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(
+ sourceSize,
+ new ResizeOptions
+ {
+ Mode = ResizeMode.Stretch,
+ Size = target
+ });
+
+ Assert.Equal(expectedSize, size);
+ Assert.Equal(expectedRectangle, rectangle);
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs
index b870ddd08a..33da33c717 100644
--- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs
+++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs
@@ -20,8 +20,8 @@ public void PadWidthHeightResizeProcessorWithCorrectOptionsSet()
this.operations.Pad(width, height);
ResizeProcessor resizeProcessor = this.Verify();
- Assert.Equal(width, resizeProcessor.Width);
- Assert.Equal(height, resizeProcessor.Height);
+ Assert.Equal(width, resizeProcessor.TargetWidth);
+ Assert.Equal(height, resizeProcessor.TargetHeight);
Assert.Equal(sampler, resizeProcessor.Sampler);
}
}
diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs
index c7ebe65e8c..f268eda86c 100644
--- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing;
@@ -18,8 +18,8 @@ public void ResizeWidthAndHeight()
this.operations.Resize(width, height);
ResizeProcessor resizeProcessor = this.Verify();
- Assert.Equal(width, resizeProcessor.Width);
- Assert.Equal(height, resizeProcessor.Height);
+ Assert.Equal(width, resizeProcessor.TargetWidth);
+ Assert.Equal(height, resizeProcessor.TargetHeight);
}
[Fact]
@@ -31,8 +31,8 @@ public void ResizeWidthAndHeightAndSampler()
this.operations.Resize(width, height, sampler);
ResizeProcessor resizeProcessor = this.Verify();
- Assert.Equal(width, resizeProcessor.Width);
- Assert.Equal(height, resizeProcessor.Height);
+ Assert.Equal(width, resizeProcessor.TargetWidth);
+ Assert.Equal(height, resizeProcessor.TargetHeight);
Assert.Equal(sampler, resizeProcessor.Sampler);
}
@@ -48,8 +48,8 @@ public void ResizeWidthAndHeightAndSamplerAndCompand()
this.operations.Resize(width, height, sampler, compand);
ResizeProcessor resizeProcessor = this.Verify();
- Assert.Equal(width, resizeProcessor.Width);
- Assert.Equal(height, resizeProcessor.Height);
+ Assert.Equal(width, resizeProcessor.TargetWidth);
+ Assert.Equal(height, resizeProcessor.TargetHeight);
Assert.Equal(sampler, resizeProcessor.Sampler);
Assert.Equal(compand, resizeProcessor.Compand);
}
@@ -74,8 +74,8 @@ public void ResizeWithOptions()
this.operations.Resize(resizeOptions);
ResizeProcessor resizeProcessor = this.Verify();
- Assert.Equal(width, resizeProcessor.Width);
- Assert.Equal(height, resizeProcessor.Height);
+ Assert.Equal(width, resizeProcessor.TargetWidth);
+ Assert.Equal(height, resizeProcessor.TargetHeight);
Assert.Equal(sampler, resizeProcessor.Sampler);
Assert.Equal(compand, resizeProcessor.Compand);
@@ -87,4 +87,4 @@ public void ResizeWithOptions()
Assert.Equal(mode, resizeOptions.Mode);
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/ImageSharp.Tests/xunit.runner.json b/tests/ImageSharp.Tests/xunit.runner.json
index 5204242f03..749ece4387 100644
--- a/tests/ImageSharp.Tests/xunit.runner.json
+++ b/tests/ImageSharp.Tests/xunit.runner.json
@@ -1,5 +1,5 @@
{
- "shadowCopy": false,
- "methodDisplay": "method",
- "diagnosticMessages": true
-}
\ No newline at end of file
+ "shadowCopy": false,
+ "methodDisplay": "method",
+ "diagnosticMessages": true
+}