Browse Source

refactor stuff + implement CalculateResizeWorkerWindowCount()

af/merge-core
Anton Firszov 7 years ago
parent
commit
07808bf8eb
  1. 13
      src/ImageSharp/Configuration.cs
  2. 339
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs
  3. 17
      tests/ImageSharp.Tests/ConfigurationTests.cs
  4. 19
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

13
src/ImageSharp/Configuration.cs

@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Memory;
namespace SixLabors.ImageSharp
@ -102,6 +103,15 @@ namespace SixLabors.ImageSharp
/// </summary>
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem();
/// <summary>
/// Gets or sets the working buffer size hint for image processors.
/// The default value is 1MB.
/// </summary>
/// <remarks>
/// Currently only used by <see cref="ResizeProcessor{TPixel}"/>.
/// </remarks>
internal int WorkingBufferSizeHintInBytes { get; set; } = 1 * 1024 * 1024;
/// <summary>
/// Gets or sets the image operations provider factory.
/// </summary>
@ -130,7 +140,8 @@ namespace SixLabors.ImageSharp
MemoryAllocator = this.MemoryAllocator,
ImageOperationsProvider = this.ImageOperationsProvider,
ReadOrigin = this.ReadOrigin,
FileSystem = this.FileSystem
FileSystem = this.FileSystem,
WorkingBufferSizeHintInBytes = this.WorkingBufferSizeHintInBytes,
};
}

339
src/ImageSharp/Processing/ResizeHelper.cs → src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs

@ -1,11 +1,13 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Linq;
using System.Numerics;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <summary>
/// Provides methods to help calculate the target rectangle when resizing using the
@ -13,6 +15,16 @@ namespace SixLabors.ImageSharp.Processing
/// </summary>
internal static class ResizeHelper
{
public static unsafe int CalculateResizeWorkerWindowCount(
int windowDiameter,
int width,
int sizeLimitHintInBytes)
{
int sizeLimitHint = sizeLimitHintInBytes / sizeof(Vector4);
int sizeOfOneWindow = windowDiameter * width;
return Math.Max(2, sizeLimitHint / sizeOfOneWindow);
}
/// <summary>
/// Calculates the target location and bounds to perform the resize operation against.
/// </summary>
@ -21,9 +33,13 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="width">The target width</param>
/// <param name="height">The target height</param>
/// <returns>
/// The <see cref="ValueTuple{Size,Rectangle}"/>.
/// The tuple representing the location and the bounds
/// </returns>
public static (Size, Rectangle) CalculateTargetLocationAndBounds(Size sourceSize, ResizeOptions options, int width, int height)
public static (Size, Rectangle) CalculateTargetLocationAndBounds(
Size sourceSize,
ResizeOptions options,
int width,
int height)
{
switch (options.Mode)
{
@ -44,7 +60,90 @@ namespace SixLabors.ImageSharp.Processing
}
}
private static (Size, Rectangle) CalculateCropRectangle(Size source, ResizeOptions options, int width, int height)
private static (Size, Rectangle) CalculateBoxPadRectangle(
Size source,
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));
}
int sourceWidth = source.Width;
int sourceHeight = source.Height;
// Fractional variants for preserving aspect ratio.
float percentHeight = MathF.Abs(height / (float)sourceHeight);
float percentWidth = MathF.Abs(width / (float)sourceWidth);
int boxPadHeight = height > 0 ? height : (int)MathF.Round(sourceHeight * percentWidth);
int boxPadWidth = width > 0 ? width : (int)MathF.Round(sourceWidth * percentHeight);
// Only calculate if upscaling.
if (sourceWidth < boxPadWidth && sourceHeight < boxPadHeight)
{
int destinationX;
int destinationY;
int destinationWidth = sourceWidth;
int destinationHeight = sourceHeight;
width = boxPadWidth;
height = boxPadHeight;
switch (options.Position)
{
case AnchorPositionMode.Left:
destinationY = (height - sourceHeight) / 2;
destinationX = 0;
break;
case AnchorPositionMode.Right:
destinationY = (height - sourceHeight) / 2;
destinationX = width - sourceWidth;
break;
case AnchorPositionMode.TopRight:
destinationY = 0;
destinationX = width - sourceWidth;
break;
case AnchorPositionMode.Top:
destinationY = 0;
destinationX = (width - sourceWidth) / 2;
break;
case AnchorPositionMode.TopLeft:
destinationY = 0;
destinationX = 0;
break;
case AnchorPositionMode.BottomRight:
destinationY = height - sourceHeight;
destinationX = width - sourceWidth;
break;
case AnchorPositionMode.Bottom:
destinationY = height - sourceHeight;
destinationX = (width - sourceWidth) / 2;
break;
case AnchorPositionMode.BottomLeft:
destinationY = height - sourceHeight;
destinationX = 0;
break;
default:
destinationY = (height - sourceHeight) / 2;
destinationX = (width - sourceWidth) / 2;
break;
}
return (new Size(width, height),
new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
}
// Switch to pad mode to downscale and calculate from there.
return CalculatePadRectangle(source, options, width, height);
}
private static (Size, Rectangle) CalculateCropRectangle(
Size source,
ResizeOptions options,
int width,
int height)
{
if (width <= 0 || height <= 0)
{
@ -147,152 +246,15 @@ namespace SixLabors.ImageSharp.Processing
destinationWidth = (int)MathF.Ceiling(sourceWidth * percentHeight);
}
return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
}
private static (Size, Rectangle) CalculatePadRectangle(Size source, 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 destinationX = 0;
int destinationY = 0;
int destinationWidth = width;
int destinationHeight = height;
// Fractional variants for preserving aspect ratio.
float percentHeight = MathF.Abs(height / (float)sourceHeight);
float percentWidth = MathF.Abs(width / (float)sourceWidth);
if (percentHeight < percentWidth)
{
ratio = percentHeight;
destinationWidth = (int)MathF.Round(sourceWidth * percentHeight);
switch (options.Position)
{
case AnchorPositionMode.Left:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.BottomLeft:
destinationX = 0;
break;
case AnchorPositionMode.Right:
case AnchorPositionMode.TopRight:
case AnchorPositionMode.BottomRight:
destinationX = (int)MathF.Round(width - (sourceWidth * ratio));
break;
default:
destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F);
break;
}
}
else
{
ratio = percentWidth;
destinationHeight = (int)MathF.Round(sourceHeight * percentWidth);
switch (options.Position)
{
case AnchorPositionMode.Top:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.TopRight:
destinationY = 0;
break;
case AnchorPositionMode.Bottom:
case AnchorPositionMode.BottomLeft:
case AnchorPositionMode.BottomRight:
destinationY = (int)MathF.Round(height - (sourceHeight * ratio));
break;
default:
destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F);
break;
}
}
return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
}
private static (Size, Rectangle) CalculateBoxPadRectangle(Size source, 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));
}
int sourceWidth = source.Width;
int sourceHeight = source.Height;
// Fractional variants for preserving aspect ratio.
float percentHeight = MathF.Abs(height / (float)sourceHeight);
float percentWidth = MathF.Abs(width / (float)sourceWidth);
int boxPadHeight = height > 0 ? height : (int)MathF.Round(sourceHeight * percentWidth);
int boxPadWidth = width > 0 ? width : (int)MathF.Round(sourceWidth * percentHeight);
// Only calculate if upscaling.
if (sourceWidth < boxPadWidth && sourceHeight < boxPadHeight)
{
int destinationX;
int destinationY;
int destinationWidth = sourceWidth;
int destinationHeight = sourceHeight;
width = boxPadWidth;
height = boxPadHeight;
switch (options.Position)
{
case AnchorPositionMode.Left:
destinationY = (height - sourceHeight) / 2;
destinationX = 0;
break;
case AnchorPositionMode.Right:
destinationY = (height - sourceHeight) / 2;
destinationX = width - sourceWidth;
break;
case AnchorPositionMode.TopRight:
destinationY = 0;
destinationX = width - sourceWidth;
break;
case AnchorPositionMode.Top:
destinationY = 0;
destinationX = (width - sourceWidth) / 2;
break;
case AnchorPositionMode.TopLeft:
destinationY = 0;
destinationX = 0;
break;
case AnchorPositionMode.BottomRight:
destinationY = height - sourceHeight;
destinationX = width - sourceWidth;
break;
case AnchorPositionMode.Bottom:
destinationY = height - sourceHeight;
destinationX = (width - sourceWidth) / 2;
break;
case AnchorPositionMode.BottomLeft:
destinationY = height - sourceHeight;
destinationX = 0;
break;
default:
destinationY = (height - sourceHeight) / 2;
destinationX = (width - sourceWidth) / 2;
break;
}
return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
}
// Switch to pad mode to downscale and calculate from there.
return CalculatePadRectangle(source, options, width, height);
return (new Size(width, height),
new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
}
private static (Size, Rectangle) CalculateMaxRectangle(Size source, ResizeOptions options, int width, int height)
private static (Size, Rectangle) CalculateMaxRectangle(
Size source,
ResizeOptions options,
int width,
int height)
{
int destinationWidth = width;
int destinationHeight = height;
@ -320,7 +282,11 @@ namespace SixLabors.ImageSharp.Processing
return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight));
}
private static (Size, Rectangle) CalculateMinRectangle(Size source, ResizeOptions options, int width, int height)
private static (Size, Rectangle) CalculateMinRectangle(
Size source,
ResizeOptions options,
int width,
int height)
{
int sourceWidth = source.Width;
int sourceHeight = source.Height;
@ -372,5 +338,78 @@ namespace SixLabors.ImageSharp.Processing
// Replace the size to match the rectangle.
return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight));
}
private static (Size, Rectangle) CalculatePadRectangle(
Size source,
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 destinationX = 0;
int destinationY = 0;
int destinationWidth = width;
int destinationHeight = height;
// Fractional variants for preserving aspect ratio.
float percentHeight = MathF.Abs(height / (float)sourceHeight);
float percentWidth = MathF.Abs(width / (float)sourceWidth);
if (percentHeight < percentWidth)
{
ratio = percentHeight;
destinationWidth = (int)MathF.Round(sourceWidth * percentHeight);
switch (options.Position)
{
case AnchorPositionMode.Left:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.BottomLeft:
destinationX = 0;
break;
case AnchorPositionMode.Right:
case AnchorPositionMode.TopRight:
case AnchorPositionMode.BottomRight:
destinationX = (int)MathF.Round(width - (sourceWidth * ratio));
break;
default:
destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F);
break;
}
}
else
{
ratio = percentWidth;
destinationHeight = (int)MathF.Round(sourceHeight * percentWidth);
switch (options.Position)
{
case AnchorPositionMode.Top:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.TopRight:
destinationY = 0;
break;
case AnchorPositionMode.Bottom:
case AnchorPositionMode.BottomLeft:
case AnchorPositionMode.BottomRight:
destinationY = (int)MathF.Round(height - (sourceHeight * ratio));
break;
default:
destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F);
break;
}
}
return (new Size(width, height),
new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight));
}
}
}

17
tests/ImageSharp.Tests/ConfigurationTests.cs

@ -39,13 +39,13 @@ namespace SixLabors.ImageSharp.Tests
/// Test that the default configuration is not null.
/// </summary>
[Fact]
public void TestDefaultConfigurationIsNotNull() => Assert.True(Configuration.Default != null);
public void TestDefaultConfigurationIsNotNull() => Assert.True(this.DefaultConfiguration != null);
/// <summary>
/// Test that the default configuration read origin options is set to begin.
/// </summary>
[Fact]
public void TestDefaultConfigurationReadOriginIsCurrent() => Assert.True(Configuration.Default.ReadOrigin == ReadOrigin.Current);
public void TestDefaultConfigurationReadOriginIsCurrent() => Assert.True(this.DefaultConfiguration.ReadOrigin == ReadOrigin.Current);
/// <summary>
/// Test that the default configuration parallel options max degrees of parallelism matches the
@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void TestDefaultConfigurationMaxDegreeOfParallelism()
{
Assert.True(Configuration.Default.MaxDegreeOfParallelism == Environment.ProcessorCount);
Assert.True(this.DefaultConfiguration.MaxDegreeOfParallelism == Environment.ProcessorCount);
var cfg = new Configuration();
Assert.True(cfg.MaxDegreeOfParallelism == Environment.ProcessorCount);
@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests
public void ConfigurationCannotAddDuplicates()
{
const int count = 4;
Configuration config = Configuration.Default;
Configuration config = this.DefaultConfiguration;
Assert.Equal(count, config.ImageFormats.Count());
@ -105,9 +105,16 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void DefaultConfigurationHasCorrectFormatCount()
{
Configuration config = Configuration.Default;
Configuration config = Configuration.CreateDefaultInstance();
Assert.Equal(4, config.ImageFormats.Count());
}
[Fact]
public void WorkingBufferSizeHint_DefaultIsCorrect()
{
Configuration config = this.DefaultConfiguration;
Assert.True(config.WorkingBufferSizeHintInBytes > 1024);
}
}
}

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

@ -35,6 +35,25 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F);
[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)]
public void CalculateResizeWorkerWindowCount(
int windowDiameter,
int width,
int sizeLimitHintInBytes,
int expectedCount)
{
int actualCount = ResizeHelper.CalculateResizeWorkerWindowCount(windowDiameter, width, sizeLimitHintInBytes);
Assert.Equal(expectedCount, actualCount);
}
[Theory]
[InlineData(-2, 0)]
[InlineData(-1, 0)]

Loading…
Cancel
Save