Browse Source

WorkingBufferSizeHintInBytes_IsAppliedCorrectly

af/merge-core
Anton Firszov 7 years ago
parent
commit
bd8c06ad12
  1. 4
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs
  2. 20
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs
  3. 69
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResamplerTests.cs
  4. 35
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs
  5. 143
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
  6. 2
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs
  7. 2
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs
  8. 2
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs
  9. 2
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs
  10. 3
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs
  11. 39
      tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs
  12. 458
      tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs
  13. 2
      tests/Images/External

4
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs

@ -16,12 +16,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
internal static class ResizeHelper
{
public static unsafe int CalculateResizeWorkerHeightInWindowBands(
int windowBandDiameter,
int windowBandHeight,
int width,
int sizeLimitHintInBytes)
{
int sizeLimitHint = sizeLimitHintInBytes / sizeof(Vector4);
int sizeOfOneWindow = windowBandDiameter * width;
int sizeOfOneWindow = windowBandHeight * width;
return Math.Max(2, sizeLimitHint / sizeOfOneWindow);
}

20
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs

@ -41,9 +41,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly Point targetOrigin;
private readonly int windowBandDiameter;
private readonly int windowBandHeight;
private readonly int windowHeight;
private readonly int workerHeight;
private RowInterval currentWindow;
@ -67,24 +67,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
this.targetWorkingRect = targetWorkingRect;
this.targetOrigin = targetOrigin;
this.windowBandDiameter = verticalKernelMap.MaxDiameter;
this.windowBandHeight = verticalKernelMap.MaxDiameter;
int numberOfWindowBands = ResizeHelper.CalculateResizeWorkerHeightInWindowBands(
this.windowBandDiameter,
this.windowBandHeight,
destWidth,
configuration.WorkingBufferSizeHintInBytes);
this.windowHeight = Math.Min(this.sourceRectangle.Height, numberOfWindowBands * this.windowBandDiameter);
this.workerHeight = Math.Min(this.sourceRectangle.Height, numberOfWindowBands * this.windowBandHeight);
this.transposedFirstPassBuffer = configuration.MemoryAllocator.Allocate2D<Vector4>(
this.windowHeight,
this.workerHeight,
destWidth,
AllocationOptions.Clean);
this.tempRowBuffer = configuration.MemoryAllocator.Allocate<Vector4>(this.sourceRectangle.Width);
this.tempColumnBuffer = configuration.MemoryAllocator.Allocate<Vector4>(destWidth);
this.currentWindow = new RowInterval(0, this.windowHeight);
this.currentWindow = new RowInterval(0, this.workerHeight);
}
public void Dispose()
@ -145,8 +145,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private void Slide()
{
int minY = this.currentWindow.Min + this.windowBandDiameter;
int maxY = Math.Min(this.currentWindow.Max + this.windowBandDiameter, this.sourceRectangle.Height);
int minY = this.currentWindow.Min + this.windowBandHeight;
int maxY = Math.Min(this.currentWindow.Max + this.windowBandHeight, this.sourceRectangle.Height);
this.currentWindow = new RowInterval(minY, maxY);
this.CalculateFirstPassValues(this.currentWindow);
}
@ -170,7 +170,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
for (int x = this.targetWorkingRect.Left; x < this.targetWorkingRect.Right; x++)
{
ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.targetOrigin.X);
firstPassSpan[x * this.windowHeight] = kernel.Convolve(tempRowSpan);
firstPassSpan[x * this.workerHeight] = kernel.Convolve(tempRowSpan);
// Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan);
}

69
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResamplerTests.cs

@ -0,0 +1,69 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
public class ResamplerTests
{
[Theory]
[InlineData(-2, 0)]
[InlineData(-1, 0)]
[InlineData(0, 1)]
[InlineData(1, 0)]
[InlineData(2, 0)]
public static void BicubicWindowOscillatesCorrectly(float x, float expected)
{
IResampler sampler = KnownResamplers.Bicubic;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
}
[Theory]
[InlineData(-2, 0)]
[InlineData(-1, 0)]
[InlineData(0, 1)]
[InlineData(1, 0)]
[InlineData(2, 0)]
public static void Lanczos3WindowOscillatesCorrectly(float x, float expected)
{
IResampler sampler = KnownResamplers.Lanczos3;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
}
[Theory]
[InlineData(-4, 0)]
[InlineData(-2, 0)]
[InlineData(0, 1)]
[InlineData(2, 0)]
[InlineData(4, 0)]
public static void Lanczos5WindowOscillatesCorrectly(float x, float expected)
{
IResampler sampler = KnownResamplers.Lanczos5;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
}
[Theory]
[InlineData(-2, 0)]
[InlineData(-1, 0)]
[InlineData(0, 1)]
[InlineData(1, 0)]
[InlineData(2, 0)]
public static void TriangleWindowOscillatesCorrectly(float x, float expected)
{
IResampler sampler = KnownResamplers.Triangle;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
}
}
}

35
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs

@ -0,0 +1,35 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using Xunit;
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)]
public void CalculateResizeWorkerHeightInWindowBands(
int windowDiameter,
int width,
int sizeLimitHintInBytes,
int expectedCount)
{
int actualCount = ResizeHelper.CalculateResizeWorkerHeightInWindowBands(windowDiameter, width, sizeLimitHintInBytes);
Assert.Equal(expectedCount, actualCount);
}
}
}

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

@ -2,12 +2,17 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.ImageSharp.Tests.Memory;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Memory;
using SixLabors.Primitives;
using Xunit;
@ -35,84 +40,6 @@ 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)]
[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,
int sizeLimitHintInBytes,
int expectedCount)
{
int actualCount = ResizeHelper.CalculateResizeWorkerHeightInWindowBands(windowDiameter, width, sizeLimitHintInBytes);
Assert.Equal(expectedCount, actualCount);
}
[Theory]
[InlineData(-2, 0)]
[InlineData(-1, 0)]
[InlineData(0, 1)]
[InlineData(1, 0)]
[InlineData(2, 0)]
public static void BicubicWindowOscillatesCorrectly(float x, float expected)
{
IResampler sampler = KnownResamplers.Bicubic;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
}
[Theory]
[InlineData(-2, 0)]
[InlineData(-1, 0)]
[InlineData(0, 1)]
[InlineData(1, 0)]
[InlineData(2, 0)]
public static void Lanczos3WindowOscillatesCorrectly(float x, float expected)
{
IResampler sampler = KnownResamplers.Lanczos3;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
}
[Theory]
[InlineData(-4, 0)]
[InlineData(-2, 0)]
[InlineData(0, 1)]
[InlineData(2, 0)]
[InlineData(4, 0)]
public static void Lanczos5WindowOscillatesCorrectly(float x, float expected)
{
IResampler sampler = KnownResamplers.Lanczos5;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
}
[Theory]
[InlineData(-2, 0)]
[InlineData(-1, 0)]
[InlineData(0, 1)]
[InlineData(1, 0)]
[InlineData(2, 0)]
public static void TriangleWindowOscillatesCorrectly(float x, float expected)
{
IResampler sampler = KnownResamplers.Triangle;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
}
[Theory]
[WithBasicTestPatternImages(15, 12, PixelTypes.Rgba32, 2, 3, 1, 2)]
[WithBasicTestPatternImages(2, 256, PixelTypes.Rgba32, 1, 1, 1, 8)]
@ -136,6 +63,66 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
}
}
private static readonly int SizeOfVector4 = Unsafe.SizeOf<Vector4>();
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, 50)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, 60)]
[WithTestPatternImages(100, 400, PixelTypes.Rgba32, 110)]
[WithTestPatternImages(79, 97, PixelTypes.Rgba32, 73)]
[WithTestPatternImages(79, 97, PixelTypes.Rgba32, 5)]
[WithTestPatternImages(47, 193, PixelTypes.Rgba32, 73)]
[WithTestPatternImages(23, 211, PixelTypes.Rgba32, 31)]
public void WorkingBufferSizeHintInBytes_IsAppliedCorrectly<TPixel>(
TestImageProvider<TPixel> provider,
int workingBufferLimitInRows)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image0 = provider.GetImage())
{
Size destSize = image0.Size() / 4;
Configuration configuration = Configuration.CreateDefaultInstance();
int workingBufferSizeHintInBytes = workingBufferLimitInRows * destSize.Width * SizeOfVector4;
TestMemoryAllocator allocator = new TestMemoryAllocator();
configuration.MemoryAllocator = allocator;
configuration.WorkingBufferSizeHintInBytes = workingBufferSizeHintInBytes;
var verticalKernelMap = ResizeKernelMap.Calculate(
KnownResamplers.Bicubic,
destSize.Height,
image0.Height,
Configuration.Default.MemoryAllocator);
int minimumWorkerAllocationInBytes = verticalKernelMap.MaxDiameter * 2 * destSize.Width * SizeOfVector4;
verticalKernelMap.Dispose();
using (Image<TPixel> image = image0.Clone(configuration))
{
image.Mutate(x => x.Resize(destSize, KnownResamplers.Bicubic, false));
image.DebugSave(
provider,
testOutputDetails: workingBufferLimitInRows,
appendPixelTypeToFileName: false);
image.CompareToReferenceOutput(
provider,
testOutputDetails: workingBufferLimitInRows,
appendPixelTypeToFileName: false);
Assert.NotEmpty(allocator.AllocationLog);
var internalAllocations = allocator.AllocationLog.Where(
e => e.ElementType == typeof(Vector4)).ToArray();
int maxAllocationSize = internalAllocations.Max(e => e.LengthInBytes);
Assert.True(maxAllocationSize <= Math.Max(workingBufferSizeHintInBytes, minimumWorkerAllocationInBytes));
}
}
}
[Theory]
[WithTestPatternImages(100, 100, DefaultPixelType)]
public void Resize_Compand<TPixel>(TestImageProvider<TPixel> provider)

2
tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests
public override Image<TPixel> GetImage()
{
var result = new Image<TPixel>(this.Width, this.Height);
var result = new Image<TPixel>(this.Configuration, this.Width, this.Height);
TPixel topLeftColor = NamedColors<TPixel>.Red;
TPixel topRightColor = NamedColors<TPixel>.Green;

2
tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests
protected int Width { get; private set; }
public override Image<TPixel> GetImage() => new Image<TPixel>(this.Width, this.Height);
public override Image<TPixel> GetImage() => new Image<TPixel>(this.Configuration, this.Width, this.Height);
public override void Deserialize(IXunitSerializationInfo info)

2
tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs

@ -152,7 +152,7 @@ namespace SixLabors.ImageSharp.Tests
Image<TPixel> cachedImage = cache.GetOrAdd(key, _ => this.LoadImage(decoder));
return cachedImage.Clone();
return cachedImage.Clone(this.Configuration);
}
public override void Deserialize(IXunitSerializationInfo info)

2
tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs

@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests
/// </summary>
public Image<TPixel> GetImage(Action<IImageProcessingContext<TPixel>> operationsToApply)
{
Image<TPixel> img = GetImage();
Image<TPixel> img = this.GetImage();
img.Mutate(operationsToApply);
return img;
}

3
tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs

@ -45,9 +45,8 @@ namespace SixLabors.ImageSharp.Tests
DrawTestPattern(image);
TestImages.Add(this.SourceFileOrDescription, image);
}
return TestImages[this.SourceFileOrDescription].Clone(this.Configuration);
}
return TestImages[this.SourceFileOrDescription].Clone();
}
/// <summary>

39
tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs

@ -1,5 +1,7 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices;
using SixLabors.Memory;
@ -8,6 +10,8 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
internal class TestMemoryAllocator : MemoryAllocator
{
private List<AllocationRequest> allocationLog = new List<AllocationRequest>();
public TestMemoryAllocator(byte dirtyValue = 42)
{
this.DirtyValue = dirtyValue;
@ -18,10 +22,11 @@ namespace SixLabors.ImageSharp.Tests.Memory
/// </summary>
public byte DirtyValue { get; }
public IList<AllocationRequest> AllocationLog => this.allocationLog;
public override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)
{
{
T[] array = this.AllocateArray<T>(length, options);
return new BasicArrayBuffer<T>(array, length);
}
@ -34,6 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
private T[] AllocateArray<T>(int length, AllocationOptions options)
where T : struct
{
this.allocationLog.Add(AllocationRequest.Create<T>(options, length));
var array = new T[length + 42];
if (options == AllocationOptions.None)
@ -44,6 +50,35 @@ namespace SixLabors.ImageSharp.Tests.Memory
return array;
}
public struct AllocationRequest
{
private AllocationRequest(Type elementType, AllocationOptions allocationOptions, int length, int lengthInBytes)
{
this.ElementType = elementType;
this.AllocationOptions = allocationOptions;
this.Length = length;
this.LengthInBytes = lengthInBytes;
if (elementType == typeof(Vector4))
{
}
}
public static AllocationRequest Create<T>(AllocationOptions allocationOptions, int length)
{
Type type = typeof(T);
int elementSize = Marshal.SizeOf(type);
return new AllocationRequest(type, allocationOptions, length, length * elementSize);
}
public Type ElementType { get; }
public AllocationOptions AllocationOptions { get; }
public int Length { get; }
public int LengthInBytes { get; }
}
/// <summary>
/// Wraps an array as an <see cref="IManagedByteBuffer"/> instance.

458
tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs

@ -4,112 +4,135 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
using Xunit.Abstractions;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
public class TestImageProviderTests
{
public static readonly TheoryData<object> BasicData = new TheoryData<object>()
{
TestImageProvider<Rgba32>.Blank(10, 20),
TestImageProvider<HalfVector4>.Blank(10, 20),
};
public static readonly TheoryData<object> FileData = new TheoryData<object>()
{
TestImageProvider<Rgba32>.File(TestImages.Bmp.Car),
TestImageProvider<HalfVector4>.File(
TestImages.Bmp.F)
};
public static string[] AllBmpFiles = { TestImages.Bmp.F, TestImages.Bmp.Bit8 };
public TestImageProviderTests(ITestOutputHelper output) => this.Output = output;
private ITestOutputHelper Output { get; }
[Theory]
[WithBlankImages(1, 1, PixelTypes.Rgba32)]
public void NoOutputSubfolderIsPresentByDefault<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel> => Assert.Empty(provider.Utility.OutputSubfolderName);
/// <summary>
/// Need to us <see cref="GenericFactory{TPixel}"/> to create instance of <see cref="Image"/> when pixelType is StandardImageClass
/// </summary>
/// <typeparam name="TPixel"></typeparam>
/// <param name="factory"></param>
/// <returns></returns>
public static Image<TPixel> CreateTestImage<TPixel>()
where TPixel : struct, IPixel<TPixel> =>
new Image<TPixel>(3, 3);
[Theory]
[WithBlankImages(42, 666, PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.HalfSingle, "hello")]
public void Use_WithEmptyImageAttribute<TPixel>(TestImageProvider<TPixel> provider, string message)
[MemberData(nameof(BasicData))]
public void Blank_MemberData<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
Image<TPixel> img = provider.GetImage();
Assert.Equal(42, img.Width);
Assert.Equal(666, img.Height);
Assert.Equal("hello", message);
Assert.True(img.Width * img.Height > 0);
}
[Theory]
[WithBlankImages(42, 666, PixelTypes.All, "hello")]
public void Use_WithBlankImagesAttribute_WithAllPixelTypes<TPixel>(
TestImageProvider<TPixel> provider,
string message)
[MemberData(nameof(FileData))]
public void File_MemberData<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
this.Output.WriteLine("SRC: " + provider.Utility.SourceFileOrDescription);
this.Output.WriteLine("OUT: " + provider.Utility.GetTestOutputFileName());
Image<TPixel> img = provider.GetImage();
Assert.Equal(42, img.Width);
Assert.Equal(666, img.Height);
Assert.Equal("hello", message);
Assert.True(img.Width * img.Height > 0);
}
[Theory]
[WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)]
[WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)]
[WithBlankImages(1, 1, PixelTypes.Argb32, PixelTypes.Argb32)]
public void PixelType_PropertyValueIsCorrect<TPixel>(TestImageProvider<TPixel> provider, PixelTypes expected)
where TPixel : struct, IPixel<TPixel> => Assert.Equal(expected, provider.PixelType);
[Theory]
[WithFile(TestImages.Bmp.Car, PixelTypes.All, 88)]
[WithFile(TestImages.Bmp.F, PixelTypes.All, 88)]
public void Use_WithFileAttribute<TPixel>(TestImageProvider<TPixel> provider, int yo)
[WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)]
public void GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache<TPixel>(
TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
if (!TestEnvironment.Is64BitProcess)
{
// We don't cache with the 32 bit build.
return;
}
Assert.NotNull(provider.Utility.SourceFileOrDescription);
Image<TPixel> img = provider.GetImage();
Assert.True(img.Width * img.Height > 0);
Assert.Equal(88, yo);
TestDecoder.DoTestThreadSafe(
() =>
{
string testName = nameof(this.GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache);
string fn = provider.Utility.GetTestOutputFileName("jpg");
this.Output.WriteLine(fn);
}
var decoder = new TestDecoder();
decoder.InitCaller(testName);
private class TestDecoder : IImageDecoder
{
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
invocationCounts[this.callerName]++;
return new Image<TPixel>(42, 42);
}
provider.GetImage(decoder);
Assert.Equal(1, TestDecoder.GetInvocationCount(testName));
// Couldn't make xUnit happy without this hackery:
provider.GetImage(decoder);
Assert.Equal(1, TestDecoder.GetInvocationCount(testName));
});
}
private static readonly ConcurrentDictionary<string, int> invocationCounts = new ConcurrentDictionary<string, int>();
[Theory]
[WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)]
public void GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual<TPixel>(
TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
Assert.NotNull(provider.Utility.SourceFileOrDescription);
private string callerName = null;
TestDecoderWithParameters.DoTestThreadSafe(
() =>
{
string testName = nameof(this
.GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual);
internal void InitCaller(string name)
{
this.callerName = name;
invocationCounts[name] = 0;
}
var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 42 };
decoder1.InitCaller(testName);
internal static int GetInvocationCount(string callerName) => invocationCounts[callerName];
var decoder2 = new TestDecoderWithParameters() { Param1 = "LoL", Param2 = 42 };
decoder2.InitCaller(testName);
private static readonly object Monitor = new object();
provider.GetImage(decoder1);
Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName));
public static void DoTestThreadSafe(Action action)
{
lock (Monitor)
{
action();
}
}
provider.GetImage(decoder2);
Assert.Equal(2, TestDecoderWithParameters.GetInvocationCount(testName));
});
}
[Theory]
[WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)]
public void GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache<TPixel>(TestImageProvider<TPixel> provider)
public void GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual<TPixel>(
TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
if (!TestEnvironment.Is64BitProcess)
@ -120,121 +143,122 @@ namespace SixLabors.ImageSharp.Tests
Assert.NotNull(provider.Utility.SourceFileOrDescription);
TestDecoder.DoTestThreadSafe(() =>
{
string testName = nameof(this.GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache);
var decoder = new TestDecoder();
decoder.InitCaller(testName);
provider.GetImage(decoder);
Assert.Equal(1, TestDecoder.GetInvocationCount(testName));
TestDecoderWithParameters.DoTestThreadSafe(
() =>
{
string testName = nameof(this
.GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual);
provider.GetImage(decoder);
Assert.Equal(1, TestDecoder.GetInvocationCount(testName));
});
}
var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 };
decoder1.InitCaller(testName);
private class TestDecoderWithParameters : IImageDecoder
{
public string Param1 { get; set; }
var decoder2 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 };
decoder2.InitCaller(testName);
public int Param2 { get; set; }
provider.GetImage(decoder1);
Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName));
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
invocationCounts[this.callerName]++;
return new Image<TPixel>(42, 42);
}
provider.GetImage(decoder2);
Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName));
});
}
private static readonly ConcurrentDictionary<string, int> invocationCounts = new ConcurrentDictionary<string, int>();
[Theory]
[WithBlankImages(1, 1, PixelTypes.Rgba32)]
public void NoOutputSubfolderIsPresentByDefault<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel> =>
Assert.Empty(provider.Utility.OutputSubfolderName);
private string callerName = null;
[Theory]
[WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)]
[WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)]
[WithBlankImages(1, 1, PixelTypes.Argb32, PixelTypes.Argb32)]
public void PixelType_PropertyValueIsCorrect<TPixel>(TestImageProvider<TPixel> provider, PixelTypes expected)
where TPixel : struct, IPixel<TPixel> =>
Assert.Equal(expected, provider.PixelType);
internal void InitCaller(string name)
[Theory]
[WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)]
public void SaveTestOutputFileMultiFrame<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
this.callerName = name;
invocationCounts[name] = 0;
}
internal static int GetInvocationCount(string callerName) => invocationCounts[callerName];
private static readonly object Monitor = new object();
string[] files = provider.Utility.SaveTestOutputFileMultiFrame(image);
public static void DoTestThreadSafe(Action action)
{
lock (Monitor)
Assert.True(files.Length > 2);
foreach (string path in files)
{
action();
this.Output.WriteLine(path);
Assert.True(File.Exists(path));
}
}
}
[Theory]
[WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)]
public void GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual<TPixel>(TestImageProvider<TPixel> provider)
[WithBasicTestPatternImages(50, 100, PixelTypes.Rgba32)]
[WithBasicTestPatternImages(49, 17, PixelTypes.Rgba32)]
[WithBasicTestPatternImages(20, 10, PixelTypes.Rgba32)]
public void Use_WithBasicTestPatternImages<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
if (!TestEnvironment.Is64BitProcess)
using (Image<TPixel> img = provider.GetImage())
{
// We don't cache with the 32 bit build.
return;
img.DebugSave(provider);
}
}
Assert.NotNull(provider.Utility.SourceFileOrDescription);
TestDecoderWithParameters.DoTestThreadSafe(() =>
{
string testName =
nameof(this.GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual);
var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 };
decoder1.InitCaller(testName);
[Theory]
[WithBlankImages(42, 666, PixelTypes.All, "hello")]
public void Use_WithBlankImagesAttribute_WithAllPixelTypes<TPixel>(
TestImageProvider<TPixel> provider,
string message)
where TPixel : struct, IPixel<TPixel>
{
Image<TPixel> img = provider.GetImage();
var decoder2 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 };
decoder2.InitCaller(testName);
Assert.Equal(42, img.Width);
Assert.Equal(666, img.Height);
Assert.Equal("hello", message);
}
provider.GetImage(decoder1);
Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName));
[Theory]
[WithBlankImages(42, 666, PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.HalfSingle, "hello")]
public void Use_WithEmptyImageAttribute<TPixel>(TestImageProvider<TPixel> provider, string message)
where TPixel : struct, IPixel<TPixel>
{
Image<TPixel> img = provider.GetImage();
provider.GetImage(decoder2);
Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName));
});
Assert.Equal(42, img.Width);
Assert.Equal(666, img.Height);
Assert.Equal("hello", message);
}
[Theory]
[WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)]
public void GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual<TPixel>(TestImageProvider<TPixel> provider)
[WithFile(TestImages.Bmp.Car, PixelTypes.All, 123)]
[WithFile(TestImages.Bmp.F, PixelTypes.All, 123)]
public void Use_WithFileAttribute<TPixel>(TestImageProvider<TPixel> provider, int yo)
where TPixel : struct, IPixel<TPixel>
{
Assert.NotNull(provider.Utility.SourceFileOrDescription);
TestDecoderWithParameters.DoTestThreadSafe(() =>
using (Image<TPixel> img = provider.GetImage())
{
string testName =
nameof(this.GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual);
var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 42 };
decoder1.InitCaller(testName);
var decoder2 = new TestDecoderWithParameters() { Param1 = "LoL", Param2 = 42 };
decoder2.InitCaller(testName);
Assert.True(img.Width * img.Height > 0);
provider.GetImage(decoder1);
Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName));
Assert.Equal(123, yo);
provider.GetImage(decoder2);
Assert.Equal(2, TestDecoderWithParameters.GetInvocationCount(testName));
});
string fn = provider.Utility.GetTestOutputFileName("jpg");
this.Output.WriteLine(fn);
}
}
public static string[] AllBmpFiles =
{
TestImages.Bmp.F,
TestImages.Bmp.Bit8
};
[Theory]
[WithFile(TestImages.Jpeg.Baseline.Testorig420, PixelTypes.Rgba32)]
public void Use_WithFileAttribute_CustomConfig<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
EnsureCustomConfigurationIsApplied(provider);
}
[Theory]
[WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.Argb32)]
@ -249,20 +273,15 @@ namespace SixLabors.ImageSharp.Tests
}
[Theory]
[WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)]
public void SaveTestOutputFileMultiFrame<TPixel>(TestImageProvider<TPixel> provider)
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.All)]
public void Use_WithMemberFactoryAttribute<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
Image<TPixel> img = provider.GetImage();
Assert.Equal(3, img.Width);
if (provider.PixelType == PixelTypes.Rgba32)
{
string[] files = provider.Utility.SaveTestOutputFileMultiFrame(image);
Assert.True(files.Length > 2);
foreach (string path in files)
{
this.Output.WriteLine(path);
Assert.True(File.Exists(path));
}
Assert.IsType<Image<Rgba32>>(img);
}
}
@ -291,89 +310,112 @@ namespace SixLabors.ImageSharp.Tests
}
}
/// <summary>
/// Need to us <see cref="GenericFactory{TPixel}"/> to create instance of <see cref="Image"/> when pixelType is StandardImageClass
/// </summary>
/// <typeparam name="TPixel"></typeparam>
/// <param name="factory"></param>
/// <returns></returns>
public static Image<TPixel> CreateTestImage<TPixel>()
where TPixel : struct, IPixel<TPixel> => new Image<TPixel>(3, 3);
[Theory]
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.All)]
public void Use_WithMemberFactoryAttribute<TPixel>(TestImageProvider<TPixel> provider)
[WithTestPatternImages(49, 20, PixelTypes.Rgba32)]
public void Use_WithTestPatternImages<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
Image<TPixel> img = provider.GetImage();
Assert.Equal(3, img.Width);
if (provider.PixelType == PixelTypes.Rgba32)
using (Image<TPixel> img = provider.GetImage())
{
Assert.IsType<Image<Rgba32>>(img);
img.DebugSave(provider);
}
}
[Theory]
[WithTestPatternImages(49,20, PixelTypes.Rgba32)]
public void Use_WithTestPatternImages<TPixel>(TestImageProvider<TPixel> provider)
[WithTestPatternImages(20, 20, PixelTypes.Rgba32)]
public void Use_WithTestPatternImages_CustomConfiguration<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> img = provider.GetImage())
{
img.DebugSave(provider);
}
EnsureCustomConfigurationIsApplied(provider);
}
[Theory]
[WithBasicTestPatternImages(50, 100, PixelTypes.Rgba32)]
[WithBasicTestPatternImages(49,17, PixelTypes.Rgba32)]
[WithBasicTestPatternImages(20, 10, PixelTypes.Rgba32)]
public void Use_WithBasicTestPatternImages<TPixel>(TestImageProvider<TPixel> provider)
private static void EnsureCustomConfigurationIsApplied<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> img = provider.GetImage())
using (var image1 = provider.GetImage())
{
img.DebugSave(provider);
var customConfiguration = Configuration.CreateDefaultInstance();
provider.Configuration = customConfiguration;
using (var image2 = provider.GetImage())
using (var image3 = provider.GetImage())
{
Assert.Same(customConfiguration, image2.GetConfiguration());
Assert.Same(customConfiguration, image3.GetConfiguration());
}
}
}
public static readonly TheoryData<object> BasicData = new TheoryData<object>()
private class TestDecoder : IImageDecoder
{
TestImageProvider<Rgba32>.Blank(10, 20),
TestImageProvider<HalfVector4>.Blank(
10,
20),
};
// Couldn't make xUnit happy without this hackery:
[Theory]
[MemberData(nameof(BasicData))]
public void Blank_MemberData<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
Image<TPixel> img = provider.GetImage();
private static readonly ConcurrentDictionary<string, int> invocationCounts =
new ConcurrentDictionary<string, int>();
Assert.True(img.Width * img.Height > 0);
private static readonly object Monitor = new object();
private string callerName = null;
public static void DoTestThreadSafe(Action action)
{
lock (Monitor)
{
action();
}
}
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
invocationCounts[this.callerName]++;
return new Image<TPixel>(42, 42);
}
internal static int GetInvocationCount(string callerName) => invocationCounts[callerName];
internal void InitCaller(string name)
{
this.callerName = name;
invocationCounts[name] = 0;
}
}
public static readonly TheoryData<object> FileData = new TheoryData<object>()
private class TestDecoderWithParameters : IImageDecoder
{
TestImageProvider<Rgba32>.File(TestImages.Bmp.Car),
TestImageProvider<HalfVector4>.File(TestImages.Bmp.F)
};
private static readonly ConcurrentDictionary<string, int> invocationCounts =
new ConcurrentDictionary<string, int>();
[Theory]
[MemberData(nameof(FileData))]
public void File_MemberData<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
this.Output.WriteLine("SRC: " + provider.Utility.SourceFileOrDescription);
this.Output.WriteLine("OUT: " + provider.Utility.GetTestOutputFileName());
private static readonly object Monitor = new object();
Image<TPixel> img = provider.GetImage();
private string callerName = null;
Assert.True(img.Width * img.Height > 0);
public string Param1 { get; set; }
public int Param2 { get; set; }
public static void DoTestThreadSafe(Action action)
{
lock (Monitor)
{
action();
}
}
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
invocationCounts[this.callerName]++;
return new Image<TPixel>(42, 42);
}
internal static int GetInvocationCount(string callerName) => invocationCounts[callerName];
internal void InitCaller(string name)
{
this.callerName = name;
invocationCounts[name] = 0;
}
}
}
}
}

2
tests/Images/External

@ -1 +1 @@
Subproject commit c7333d2a81a74d1936bd202bcb6b16cbfe6bcdce
Subproject commit 6db7ed95dd8a2de4caa6d885952b6689d026ad27
Loading…
Cancel
Save