diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs index 595a7e852e..956e6b84e7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs +++ b/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); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 339e8a3b2c..ce28a00ee7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/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( - this.windowHeight, + this.workerHeight, destWidth, AllocationOptions.Clean); this.tempRowBuffer = configuration.MemoryAllocator.Allocate(this.sourceRectangle.Width); this.tempColumnBuffer = configuration.MemoryAllocator.Allocate(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); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResamplerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResamplerTests.cs new file mode 100644 index 0000000000..b7b4597c79 --- /dev/null +++ b/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); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs new file mode 100644 index 0000000000..b0d8ef6531 --- /dev/null +++ b/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); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 5325581662..2ffb42916f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/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(); + + [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( + TestImageProvider provider, + int workingBufferLimitInRows) + where TPixel : struct, IPixel + { + using (Image 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 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(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs index 47bb22aeb3..de203535c6 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BasicTestPatternProvider.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests public override Image GetImage() { - var result = new Image(this.Width, this.Height); + var result = new Image(this.Configuration, this.Width, this.Height); TPixel topLeftColor = NamedColors.Red; TPixel topRightColor = NamedColors.Green; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs index 1275e522f8..dae2f0cfe4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests protected int Width { get; private set; } - public override Image GetImage() => new Image(this.Width, this.Height); + public override Image GetImage() => new Image(this.Configuration, this.Width, this.Height); public override void Deserialize(IXunitSerializationInfo info) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 3ed696c472..8c5b88b280 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -152,7 +152,7 @@ namespace SixLabors.ImageSharp.Tests Image cachedImage = cache.GetOrAdd(key, _ => this.LoadImage(decoder)); - return cachedImage.Clone(); + return cachedImage.Clone(this.Configuration); } public override void Deserialize(IXunitSerializationInfo info) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 52f66a78b5..15fab9b2bf 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests /// public Image GetImage(Action> operationsToApply) { - Image img = GetImage(); + Image img = this.GetImage(); img.Mutate(operationsToApply); return img; } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 336cdbf11f..6df8c85016 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/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(); } /// diff --git a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs index dc755e6827..5613e7b684 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs +++ b/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 allocationLog = new List(); + public TestMemoryAllocator(byte dirtyValue = 42) { this.DirtyValue = dirtyValue; @@ -18,10 +22,11 @@ namespace SixLabors.ImageSharp.Tests.Memory /// public byte DirtyValue { get; } + public IList AllocationLog => this.allocationLog; + public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) - { + { T[] array = this.AllocateArray(length, options); - return new BasicArrayBuffer(array, length); } @@ -34,6 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Memory private T[] AllocateArray(int length, AllocationOptions options) where T : struct { + this.allocationLog.Add(AllocationRequest.Create(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(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; } + } + /// /// Wraps an array as an instance. diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 1bee34f1a7..4ef6a582c9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/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 BasicData = new TheoryData() + { + TestImageProvider.Blank(10, 20), + TestImageProvider.Blank(10, 20), + }; + + public static readonly TheoryData FileData = new TheoryData() + { + TestImageProvider.File(TestImages.Bmp.Car), + TestImageProvider.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(TestImageProvider provider) - where TPixel : struct, IPixel => Assert.Empty(provider.Utility.OutputSubfolderName); + /// + /// Need to us to create instance of when pixelType is StandardImageClass + /// + /// + /// + /// + public static Image CreateTestImage() + where TPixel : struct, IPixel => + new Image(3, 3); [Theory] - [WithBlankImages(42, 666, PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.HalfSingle, "hello")] - public void Use_WithEmptyImageAttribute(TestImageProvider provider, string message) + [MemberData(nameof(BasicData))] + public void Blank_MemberData(TestImageProvider provider) where TPixel : struct, IPixel { Image 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( - TestImageProvider provider, - string message) + [MemberData(nameof(FileData))] + public void File_MemberData(TestImageProvider provider) where TPixel : struct, IPixel { + this.Output.WriteLine("SRC: " + provider.Utility.SourceFileOrDescription); + this.Output.WriteLine("OUT: " + provider.Utility.GetTestOutputFileName()); + Image 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(TestImageProvider provider, PixelTypes expected) - where TPixel : struct, IPixel => Assert.Equal(expected, provider.PixelType); - - [Theory] - [WithFile(TestImages.Bmp.Car, PixelTypes.All, 88)] - [WithFile(TestImages.Bmp.F, PixelTypes.All, 88)] - public void Use_WithFileAttribute(TestImageProvider provider, int yo) + [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] + public void GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache( + TestImageProvider provider) where TPixel : struct, IPixel { + if (!TestEnvironment.Is64BitProcess) + { + // We don't cache with the 32 bit build. + return; + } + Assert.NotNull(provider.Utility.SourceFileOrDescription); - Image 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 Decode(Configuration configuration, Stream stream) - where TPixel : struct, IPixel - { - invocationCounts[this.callerName]++; - return new Image(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 invocationCounts = new ConcurrentDictionary(); + [Theory] + [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] + public void GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual( + TestImageProvider provider) + where TPixel : struct, IPixel + { + 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(TestImageProvider provider) + public void GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual( + TestImageProvider provider) where TPixel : struct, IPixel { 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 Decode(Configuration configuration, Stream stream) - where TPixel : struct, IPixel - { - invocationCounts[this.callerName]++; - return new Image(42, 42); - } + provider.GetImage(decoder2); + Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); + }); + } - private static readonly ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); + [Theory] + [WithBlankImages(1, 1, PixelTypes.Rgba32)] + public void NoOutputSubfolderIsPresentByDefault(TestImageProvider provider) + where TPixel : struct, IPixel => + 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(TestImageProvider provider, PixelTypes expected) + where TPixel : struct, IPixel => + Assert.Equal(expected, provider.PixelType); - internal void InitCaller(string name) + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void SaveTestOutputFileMultiFrame(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image 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(TestImageProvider provider) + [WithBasicTestPatternImages(50, 100, PixelTypes.Rgba32)] + [WithBasicTestPatternImages(49, 17, PixelTypes.Rgba32)] + [WithBasicTestPatternImages(20, 10, PixelTypes.Rgba32)] + public void Use_WithBasicTestPatternImages(TestImageProvider provider) where TPixel : struct, IPixel { - if (!TestEnvironment.Is64BitProcess) + using (Image 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( + TestImageProvider provider, + string message) + where TPixel : struct, IPixel + { + Image 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(TestImageProvider provider, string message) + where TPixel : struct, IPixel + { + Image 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(TestImageProvider provider) + [WithFile(TestImages.Bmp.Car, PixelTypes.All, 123)] + [WithFile(TestImages.Bmp.F, PixelTypes.All, 123)] + public void Use_WithFileAttribute(TestImageProvider provider, int yo) where TPixel : struct, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); - - TestDecoderWithParameters.DoTestThreadSafe(() => + using (Image 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(TestImageProvider provider) + where TPixel : struct, IPixel + { + 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(TestImageProvider provider) + [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All)] + public void Use_WithMemberFactoryAttribute(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + Image 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>(img); } } @@ -291,89 +310,112 @@ namespace SixLabors.ImageSharp.Tests } } - /// - /// Need to us to create instance of when pixelType is StandardImageClass - /// - /// - /// - /// - public static Image CreateTestImage() - where TPixel : struct, IPixel => new Image(3, 3); - [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All)] - public void Use_WithMemberFactoryAttribute(TestImageProvider provider) + [WithTestPatternImages(49, 20, PixelTypes.Rgba32)] + public void Use_WithTestPatternImages(TestImageProvider provider) where TPixel : struct, IPixel { - Image img = provider.GetImage(); - Assert.Equal(3, img.Width); - if (provider.PixelType == PixelTypes.Rgba32) + using (Image img = provider.GetImage()) { - Assert.IsType>(img); + img.DebugSave(provider); } - } - + [Theory] - [WithTestPatternImages(49,20, PixelTypes.Rgba32)] - public void Use_WithTestPatternImages(TestImageProvider provider) + [WithTestPatternImages(20, 20, PixelTypes.Rgba32)] + public void Use_WithTestPatternImages_CustomConfiguration(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image 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(TestImageProvider provider) + private static void EnsureCustomConfigurationIsApplied(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image 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 BasicData = new TheoryData() + private class TestDecoder : IImageDecoder { - TestImageProvider.Blank(10, 20), - TestImageProvider.Blank( - 10, - 20), - }; + // Couldn't make xUnit happy without this hackery: - [Theory] - [MemberData(nameof(BasicData))] - public void Blank_MemberData(TestImageProvider provider) - where TPixel : struct, IPixel - { - Image img = provider.GetImage(); + private static readonly ConcurrentDictionary invocationCounts = + new ConcurrentDictionary(); - 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 Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + invocationCounts[this.callerName]++; + return new Image(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 FileData = new TheoryData() + private class TestDecoderWithParameters : IImageDecoder { - TestImageProvider.File(TestImages.Bmp.Car), - TestImageProvider.File(TestImages.Bmp.F) - }; + private static readonly ConcurrentDictionary invocationCounts = + new ConcurrentDictionary(); - [Theory] - [MemberData(nameof(FileData))] - public void File_MemberData(TestImageProvider provider) - where TPixel : struct, IPixel - { - this.Output.WriteLine("SRC: " + provider.Utility.SourceFileOrDescription); - this.Output.WriteLine("OUT: " + provider.Utility.GetTestOutputFileName()); + private static readonly object Monitor = new object(); - Image 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 Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + invocationCounts[this.callerName]++; + return new Image(42, 42); + } + + internal static int GetInvocationCount(string callerName) => invocationCounts[callerName]; + + internal void InitCaller(string name) + { + this.callerName = name; + invocationCounts[name] = 0; + } } } -} +} \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index c7333d2a81..6db7ed95dd 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit c7333d2a81a74d1936bd202bcb6b16cbfe6bcdce +Subproject commit 6db7ed95dd8a2de4caa6d885952b6689d026ad27