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.Formats.Png;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
@ -102,6 +103,15 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); 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> /// <summary>
/// Gets or sets the image operations provider factory. /// Gets or sets the image operations provider factory.
/// </summary> /// </summary>
@ -130,7 +140,8 @@ namespace SixLabors.ImageSharp
MemoryAllocator = this.MemoryAllocator, MemoryAllocator = this.MemoryAllocator,
ImageOperationsProvider = this.ImageOperationsProvider, ImageOperationsProvider = this.ImageOperationsProvider,
ReadOrigin = this.ReadOrigin, 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. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Linq; using System.Linq;
using System.Numerics;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
/// <summary> /// <summary>
/// Provides methods to help calculate the target rectangle when resizing using the /// Provides methods to help calculate the target rectangle when resizing using the
@ -13,6 +15,16 @@ namespace SixLabors.ImageSharp.Processing
/// </summary> /// </summary>
internal static class ResizeHelper 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> /// <summary>
/// Calculates the target location and bounds to perform the resize operation against. /// Calculates the target location and bounds to perform the resize operation against.
/// </summary> /// </summary>
@ -21,9 +33,13 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="width">The target width</param> /// <param name="width">The target width</param>
/// <param name="height">The target height</param> /// <param name="height">The target height</param>
/// <returns> /// <returns>
/// The <see cref="ValueTuple{Size,Rectangle}"/>. /// The tuple representing the location and the bounds
/// </returns> /// </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) 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) if (width <= 0 || height <= 0)
{ {
@ -147,152 +246,15 @@ namespace SixLabors.ImageSharp.Processing
destinationWidth = (int)MathF.Ceiling(sourceWidth * percentHeight); destinationWidth = (int)MathF.Ceiling(sourceWidth * percentHeight);
} }
return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); 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);
} }
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 destinationWidth = width;
int destinationHeight = height; int destinationHeight = height;
@ -320,7 +282,11 @@ namespace SixLabors.ImageSharp.Processing
return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight)); 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 sourceWidth = source.Width;
int sourceHeight = source.Height; int sourceHeight = source.Height;
@ -372,5 +338,78 @@ namespace SixLabors.ImageSharp.Processing
// Replace the size to match the rectangle. // Replace the size to match the rectangle.
return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight)); 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. /// Test that the default configuration is not null.
/// </summary> /// </summary>
[Fact] [Fact]
public void TestDefaultConfigurationIsNotNull() => Assert.True(Configuration.Default != null); public void TestDefaultConfigurationIsNotNull() => Assert.True(this.DefaultConfiguration != null);
/// <summary> /// <summary>
/// Test that the default configuration read origin options is set to begin. /// Test that the default configuration read origin options is set to begin.
/// </summary> /// </summary>
[Fact] [Fact]
public void TestDefaultConfigurationReadOriginIsCurrent() => Assert.True(Configuration.Default.ReadOrigin == ReadOrigin.Current); public void TestDefaultConfigurationReadOriginIsCurrent() => Assert.True(this.DefaultConfiguration.ReadOrigin == ReadOrigin.Current);
/// <summary> /// <summary>
/// Test that the default configuration parallel options max degrees of parallelism matches the /// Test that the default configuration parallel options max degrees of parallelism matches the
@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void TestDefaultConfigurationMaxDegreeOfParallelism() public void TestDefaultConfigurationMaxDegreeOfParallelism()
{ {
Assert.True(Configuration.Default.MaxDegreeOfParallelism == Environment.ProcessorCount); Assert.True(this.DefaultConfiguration.MaxDegreeOfParallelism == Environment.ProcessorCount);
var cfg = new Configuration(); var cfg = new Configuration();
Assert.True(cfg.MaxDegreeOfParallelism == Environment.ProcessorCount); Assert.True(cfg.MaxDegreeOfParallelism == Environment.ProcessorCount);
@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests
public void ConfigurationCannotAddDuplicates() public void ConfigurationCannotAddDuplicates()
{ {
const int count = 4; const int count = 4;
Configuration config = Configuration.Default; Configuration config = this.DefaultConfiguration;
Assert.Equal(count, config.ImageFormats.Count()); Assert.Equal(count, config.ImageFormats.Count());
@ -105,9 +105,16 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void DefaultConfigurationHasCorrectFormatCount() public void DefaultConfigurationHasCorrectFormatCount()
{ {
Configuration config = Configuration.Default; Configuration config = Configuration.CreateDefaultInstance();
Assert.Equal(4, config.ImageFormats.Count()); 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); 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] [Theory]
[InlineData(-2, 0)] [InlineData(-2, 0)]
[InlineData(-1, 0)] [InlineData(-1, 0)]

Loading…
Cancel
Save