Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 107 additions & 121 deletions src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,28 @@ public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSiz
{
Guard.NotNull(sampler, nameof(sampler));

// Ensure size is populated across both dimensions.
// Ensure target 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;
const int Min = 1;
if (width == 0 && height > 0)
{
width = (int)MathF.Max(min, MathF.Round(sourceSize.Width * height / (float)sourceSize.Height));
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));
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.TargetWidth = width;
this.TargetHeight = height;
this.TargetRectangle = targetRectangle;
this.Compand = compand;
}
Expand All @@ -62,32 +62,11 @@ 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;
}
Expand All @@ -112,12 +91,12 @@ public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSiz
/// <summary>
/// Gets the target width.
/// </summary>
public int Width { get; }
public int TargetWidth { get; }

/// <summary>
/// Gets the target height.
/// </summary>
public int Height { get; }
public int TargetHeight { get; }

/// <summary>
/// Gets the resize rectangle.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ public ResizeProcessor(ResizeProcessor parameterSource, Image<TPixel> source, Re
/// <summary>
/// Gets the target width.
/// </summary>
public int Width => this.parameterSource.Width;
public int TargetWidth => this.parameterSource.TargetWidth;

/// <summary>
/// Gets the target height.
/// </summary>
public int Height => this.parameterSource.Height;
public int TargetHeight => this.parameterSource.TargetHeight;

/// <summary>
/// Gets the resize rectangle.
/// Gets the target resize rectangle.
/// </summary>
public Rectangle TargetRectangle => this.parameterSource.TargetRectangle;

Expand All @@ -80,8 +80,8 @@ protected override Image<TPixel> CreateDestination()
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
x => new ImageFrame<TPixel>(
configuration,
this.Width,
this.Height,
this.TargetWidth,
this.TargetHeight,
x.Metadata.DeepClone()));

// Use the overload to prevent an extra frame being added
Expand Down Expand Up @@ -128,8 +128,8 @@ protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixe
return;
}

int width = this.Width;
int height = this.Height;
int width = this.TargetWidth;
int height = this.TargetHeight;
int sourceX = sourceRectangle.X;
int sourceY = sourceRectangle.Y;
int startY = this.TargetRectangle.Y;
Expand Down
8 changes: 3 additions & 5 deletions src/ImageSharp/Processing/ResizeOptions.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -26,7 +24,7 @@ public class ResizeOptions
/// <summary>
/// Gets or sets the center coordinates.
/// </summary>
public IEnumerable<float> CenterCoordinates { get; set; } = Array.Empty<float>();
public PointF? CenterCoordinates { get; set; }

/// <summary>
/// Gets or sets the target size.
Expand All @@ -44,4 +42,4 @@ public class ResizeOptions
/// </summary>
public bool Compand { get; set; } = false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
}
}
}
}
4 changes: 2 additions & 2 deletions tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public void PadWidthHeightResizeProcessorWithCorrectOptionsSet()
this.operations.Pad(width, height);
ResizeProcessor resizeProcessor = this.Verify<ResizeProcessor>();

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);
}
}
Expand Down
20 changes: 10 additions & 10 deletions tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -18,8 +18,8 @@ public void ResizeWidthAndHeight()
this.operations.Resize(width, height);
ResizeProcessor resizeProcessor = this.Verify<ResizeProcessor>();

Assert.Equal(width, resizeProcessor.Width);
Assert.Equal(height, resizeProcessor.Height);
Assert.Equal(width, resizeProcessor.TargetWidth);
Assert.Equal(height, resizeProcessor.TargetHeight);
}

[Fact]
Expand All @@ -31,8 +31,8 @@ public void ResizeWidthAndHeightAndSampler()
this.operations.Resize(width, height, sampler);
ResizeProcessor resizeProcessor = this.Verify<ResizeProcessor>();

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);
}

Expand All @@ -48,8 +48,8 @@ public void ResizeWidthAndHeightAndSamplerAndCompand()
this.operations.Resize(width, height, sampler, compand);
ResizeProcessor resizeProcessor = this.Verify<ResizeProcessor>();

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);
}
Expand All @@ -74,8 +74,8 @@ public void ResizeWithOptions()
this.operations.Resize(resizeOptions);
ResizeProcessor resizeProcessor = this.Verify<ResizeProcessor>();

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);

Expand All @@ -87,4 +87,4 @@ public void ResizeWithOptions()
Assert.Equal(mode, resizeOptions.Mode);
}
}
}
}
8 changes: 4 additions & 4 deletions tests/ImageSharp.Tests/xunit.runner.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"shadowCopy": false,
"methodDisplay": "method",
"diagnosticMessages": true
}
"shadowCopy": false,
"methodDisplay": "method",
"diagnosticMessages": true
}