Browse Source

Merge pull request #719 from Davidsv/ImageSharp-issue-711-resize-aspect-ratio-or-closest

Keep one pixel if aspect ratio respecting resize would set a dimension to 0.
af/merge-core
James Jackson-South 7 years ago
committed by GitHub
parent
commit
fe88a908d4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  2. 20
      src/ImageSharp/Processing/ResizeExtensions.cs
  3. 34
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

28
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

@ -40,25 +40,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Guard.NotNull(options, nameof(options));
Guard.NotNull(options.Sampler, nameof(options.Sampler));
int tempWidth = options.Size.Width;
int tempHeight = options.Size.Height;
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 (tempWidth == 0 && tempHeight > 0)
// 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)
{
tempWidth = (int)MathF.Round(sourceSize.Width * tempHeight / (float)sourceSize.Height);
targetWidth = (int)MathF.Max(min, MathF.Round(sourceSize.Width * targetHeight / (float)sourceSize.Height));
}
if (tempHeight == 0 && tempWidth > 0)
if (targetHeight == 0 && targetWidth > 0)
{
tempHeight = (int)MathF.Round(sourceSize.Height * tempWidth / (float)sourceSize.Width);
targetHeight = (int)MathF.Max(min, MathF.Round(sourceSize.Height * targetWidth / (float)sourceSize.Width));
}
Guard.MustBeGreaterThan(tempWidth, 0, nameof(tempWidth));
Guard.MustBeGreaterThan(tempHeight, 0, nameof(tempHeight));
Guard.MustBeGreaterThan(targetWidth, 0, nameof(targetWidth));
Guard.MustBeGreaterThan(targetHeight, 0, nameof(targetHeight));
(Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, tempWidth, tempHeight);
(Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, targetWidth, targetHeight);
this.Sampler = options.Sampler;
this.Width = size.Width;
@ -95,15 +98,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
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.Round(sourceSize.Width * height / (float)sourceSize.Height);
width = (int)MathF.Max(min, MathF.Round(sourceSize.Width * height / (float)sourceSize.Height));
resizeRectangle.Width = width;
}
if (height == 0 && width > 0)
{
height = (int)MathF.Round(sourceSize.Height * width / (float)sourceSize.Width);
height = (int)MathF.Max(min, MathF.Round(sourceSize.Height * width / (float)sourceSize.Width));
resizeRectangle.Height = height;
}

20
src/ImageSharp/Processing/ResizeExtensions.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="source">The image to resize.</param>
/// <param name="options">The resize options.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
/// <remarks>Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image</remarks>
/// <remarks>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.</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, ResizeOptions options)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new ResizeProcessor<TPixel>(options, source.GetCurrentSize()));
@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="source">The image to resize.</param>
/// <param name="size">The target image size.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio.</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, Size size)
where TPixel : struct, IPixel<TPixel>
=> Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false);
@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="size">The target image size.</param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio.</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, Size size, bool compand)
where TPixel : struct, IPixel<TPixel>
=> Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand);
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="width">The target image width.</param>
/// <param name="height">The target image height.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio.</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, int width, int height)
where TPixel : struct, IPixel<TPixel>
=> Resize(source, width, height, KnownResamplers.Bicubic, false);
@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="height">The target image height.</param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio.</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, int width, int height, bool compand)
where TPixel : struct, IPixel<TPixel>
=> Resize(source, width, height, KnownResamplers.Bicubic, compand);
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="height">The target image height.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio.</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, int width, int height, IResampler sampler)
where TPixel : struct, IPixel<TPixel>
=> Resize(source, width, height, sampler, false);
@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio.</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, Size size, IResampler sampler, bool compand)
where TPixel : struct, IPixel<TPixel>
=> Resize(source, size.Width, size.Height, sampler, new Rectangle(0, 0, size.Width, size.Height), compand);
@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio.</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, int width, int height, IResampler sampler, bool compand)
where TPixel : struct, IPixel<TPixel>
=> Resize(source, width, height, sampler, new Rectangle(0, 0, width, height), compand);
@ -136,7 +136,7 @@ namespace SixLabors.ImageSharp.Processing
/// </param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio.</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(
this IImageProcessingContext<TPixel> source,
int width,
@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.Processing
/// </param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio.</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(
this IImageProcessingContext<TPixel> source,
int width,

34
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

@ -178,6 +178,32 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
}
}
[Theory]
[WithTestPatternImages(100, 10, DefaultPixelType)]
public void ResizeWidthCannotKeepAspectKeepsOnePixel<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
image.Mutate(x => x.Resize(5, 0));
Assert.Equal(5, image.Width);
Assert.Equal(1, image.Height);
}
}
[Theory]
[WithTestPatternImages(10, 100, DefaultPixelType)]
public void ResizeHeightCannotKeepAspectKeepsOnePixel<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
image.Mutate(x => x.Resize(0, 5));
Assert.Equal(1, image.Width);
Assert.Equal(5, image.Height);
}
}
[Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType)]
public void ResizeWithCropWidthMode<TPixel>(TestImageProvider<TPixel> provider)
@ -324,7 +350,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[InlineData(2, 0)]
public static void BicubicWindowOscillatesCorrectly(float x, float expected)
{
var sampler = KnownResamplers.Bicubic;
IResampler sampler = KnownResamplers.Bicubic;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
@ -338,7 +364,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[InlineData(2, 0)]
public static void TriangleWindowOscillatesCorrectly(float x, float expected)
{
var sampler = KnownResamplers.Triangle;
IResampler sampler = KnownResamplers.Triangle;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
@ -352,7 +378,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[InlineData(2, 0)]
public static void Lanczos3WindowOscillatesCorrectly(float x, float expected)
{
var sampler = KnownResamplers.Lanczos3;
IResampler sampler = KnownResamplers.Lanczos3;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
@ -366,7 +392,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[InlineData(4, 0)]
public static void Lanczos5WindowOscillatesCorrectly(float x, float expected)
{
var sampler = KnownResamplers.Lanczos5;
IResampler sampler = KnownResamplers.Lanczos5;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);

Loading…
Cancel
Save