From 2e6ac845ed3f5c290c4590c966614bd1a272724b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 26 Aug 2020 15:14:08 +0100 Subject: [PATCH 01/25] Encode then decode. --- src/ImageSharp/Advanced/AotCompilerTools.cs | 73 +++++++++------------ 1 file changed, 30 insertions(+), 43 deletions(-) diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 2ea456286f..dc0c59e7bf 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -3,12 +3,12 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -27,14 +27,14 @@ namespace SixLabors.ImageSharp.Advanced { static AotCompilerTools() { - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); } /// @@ -90,10 +90,7 @@ namespace SixLabors.ImageSharp.Advanced Unsafe.SizeOf(); - AotCodec(new Formats.Png.PngDecoder(), new Formats.Png.PngEncoder()); - AotCodec(new Formats.Bmp.BmpDecoder(), new Formats.Bmp.BmpEncoder()); - AotCodec(new Formats.Gif.GifDecoder(), new Formats.Gif.GifEncoder()); - AotCodec(new Formats.Jpeg.JpegDecoder(), new Formats.Jpeg.JpegEncoder()); + AotCodecs(); // TODO: Do the discovery work to figure out what works and what doesn't. } @@ -112,11 +109,9 @@ namespace SixLabors.ImageSharp.Advanced private static void AotCompileOctreeQuantizer() where TPixel : unmanaged, IPixel { - using (var test = new OctreeQuantizer(Configuration.Default, new OctreeQuantizer().Options)) - { - var frame = new ImageFrame(Configuration.Default, 1, 1); - test.QuantizeFrame(frame, frame.Bounds()); - } + using var test = new OctreeQuantizer(Configuration.Default, new OctreeQuantizer().Options); + var frame = new ImageFrame(Configuration.Default, 1, 1); + test.QuantizeFrame(frame, frame.Bounds()); } /// @@ -126,11 +121,9 @@ namespace SixLabors.ImageSharp.Advanced private static void AotCompileWuQuantizer() where TPixel : unmanaged, IPixel { - using (var test = new WuQuantizer(Configuration.Default, new WuQuantizer().Options)) - { - var frame = new ImageFrame(Configuration.Default, 1, 1); - test.QuantizeFrame(frame, frame.Bounds()); - } + using var test = new WuQuantizer(Configuration.Default, new WuQuantizer().Options); + var frame = new ImageFrame(Configuration.Default, 1, 1); + test.QuantizeFrame(frame, frame.Bounds()); } /// @@ -140,11 +133,9 @@ namespace SixLabors.ImageSharp.Advanced private static void AotCompilePaletteQuantizer() where TPixel : unmanaged, IPixel { - using (var test = (PaletteQuantizer)new PaletteQuantizer(Array.Empty()).CreatePixelSpecificQuantizer(Configuration.Default)) - { - var frame = new ImageFrame(Configuration.Default, 1, 1); - test.QuantizeFrame(frame, frame.Bounds()); - } + using var test = (PaletteQuantizer)new PaletteQuantizer(Array.Empty()).CreatePixelSpecificQuantizer(Configuration.Default); + var frame = new ImageFrame(Configuration.Default, 1, 1); + test.QuantizeFrame(frame, frame.Bounds()); } /// @@ -167,26 +158,22 @@ namespace SixLabors.ImageSharp.Advanced /// /// This method pre-seeds the decoder and encoder for a given pixel format in the AoT compiler for iOS. /// - /// The image decoder to seed. - /// The image encoder to seed. /// The pixel format. - private static void AotCodec(IImageDecoder decoder, IImageEncoder encoder) + private static void AotCodecs() where TPixel : unmanaged, IPixel { - try + Configuration configuration = Configuration.Default; + ImageFormatManager formatsManager = configuration.ImageFormatsManager; + foreach (IImageFormat imageFormat in configuration.ImageFormats) { - decoder.Decode(Configuration.Default, null); - } - catch - { - } + using var ms = new MemoryStream(); + using (var encoded = new Image(1, 1)) + { + encoded.Save(ms, formatsManager.FindEncoder(imageFormat)); + ms.Position = 0; + } - try - { - encoder.Encode(null, null); - } - catch - { + using var decoded = Image.Load(ms); } } From 0fa3921de620b49778e250d2a6b88a6a8a6dd9be Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 7 Sep 2020 12:36:18 +0100 Subject: [PATCH 02/25] Add all IPixel interface methods. --- src/ImageSharp/Advanced/AotCompilerTools.cs | 27 ++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index dc0c59e7bf..666eb513f1 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -82,6 +82,7 @@ namespace SixLabors.ImageSharp.Advanced where TPixel : unmanaged, IPixel { // This is we actually call all the individual methods you need to seed. + AotPixelInterface(); AotCompileOctreeQuantizer(); AotCompileWuQuantizer(); AotCompilePaletteQuantizer(); @@ -95,6 +96,30 @@ namespace SixLabors.ImageSharp.Advanced // TODO: Do the discovery work to figure out what works and what doesn't. } + private static void AotPixelInterface() + where TPixel : unmanaged, IPixel + { + TPixel pixel = default; + Rgba32 rgba32 = default; + pixel.ToRgba32(ref rgba32); + pixel.FromRgba32(rgba32); + pixel.FromScaledVector4(pixel.ToScaledVector4()); + pixel.FromVector4(pixel.ToVector4()); + + pixel.FromArgb32(default); + pixel.FromBgr24(default); + pixel.FromBgra32(default); + pixel.FromBgra5551(default); + pixel.FromL16(default); + pixel.FromL8(default); + pixel.FromLa16(default); + pixel.FromLa32(default); + pixel.FromRgb24(default); + pixel.FromRgb48(default); + pixel.FromRgba64(default); + pixel.FromRgb24(default); + } + /// /// This method doesn't actually do anything but serves an important purpose... /// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an exception: @@ -107,7 +132,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. private static void AotCompileOctreeQuantizer() - where TPixel : unmanaged, IPixel + where TPixel : unmanaged, IPixel { using var test = new OctreeQuantizer(Configuration.Default, new OctreeQuantizer().Options); var frame = new ImageFrame(Configuration.Default, 1, 1); From 65d103ed65f99b8ce1ef52d0f1164cfa8de4a810 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 8 Sep 2020 08:53:50 +0100 Subject: [PATCH 03/25] Update AotCompilerTools.cs --- src/ImageSharp/Advanced/AotCompilerTools.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 666eb513f1..ed23cd445a 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -198,7 +198,8 @@ namespace SixLabors.ImageSharp.Advanced ms.Position = 0; } - using var decoded = Image.Load(ms); + using var decoded = Image.Load(ms); + Span span = decoded.GetPixelRowSpan(0); } } From 72960ec97949d87a7ed4a74316fe6595450c3028 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 1 Mar 2021 14:42:51 +0000 Subject: [PATCH 04/25] Fix ordered dither output for small palette lengths. --- src/ImageSharp/ImageSharp.csproj | 4 +- src/ImageSharp/Processing/KnownDitherings.cs | 5 ++ .../Processors/Dithering/ErrorDither.cs | 31 ++++---- .../Dithering/OrderedDither.KnownTypes.cs | 5 ++ .../Processors/Dithering/OrderedDither.cs | 75 ++++++++++--------- .../Processors/Dithering/DitherTests.cs | 1 + .../Processors/Quantization/QuantizerTests.cs | 2 +- .../TestUtilities/TestUtils.cs | 2 +- 8 files changed, 71 insertions(+), 54 deletions(-) diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index ca6ca16898..832d551fd7 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -31,8 +31,8 @@ - - + + diff --git a/src/ImageSharp/Processing/KnownDitherings.cs b/src/ImageSharp/Processing/KnownDitherings.cs index 1c086b7408..9537acda18 100644 --- a/src/ImageSharp/Processing/KnownDitherings.cs +++ b/src/ImageSharp/Processing/KnownDitherings.cs @@ -30,6 +30,11 @@ namespace SixLabors.ImageSharp.Processing /// public static IDither Bayer8x8 { get; } = OrderedDither.Bayer8x8; + /// + /// Gets the order ditherer using the 16x16 Bayer dithering matrix + /// + public static IDither Bayer16x16 { get; } = OrderedDither.Bayer16x16; + /// /// Gets the error Dither that implements the Atkinson algorithm. /// diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs index 30ac5f135b..b853fcb669 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// /// The diffusion matrix. /// The starting offset within the matrix. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ErrorDither(in DenseMatrix matrix, int offset) { this.matrix = matrix; @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering => !(left == right); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyQuantizationDither( ref TFrameQuantizer quantizer, ImageFrame source, @@ -96,26 +96,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering where TFrameQuantizer : struct, IQuantizer where TPixel : unmanaged, IPixel { - int offsetY = bounds.Top; - int offsetX = bounds.Left; float scale = quantizer.Options.DitherScale; for (int y = bounds.Top; y < bounds.Bottom; y++) { - ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); - ref byte destinationRowRef = ref MemoryMarshal.GetReference(destination.GetWritablePixelRowSpanUnsafe(y - offsetY)); + ReadOnlySpan sourceRow = source.GetPixelRowSpan(y).Slice(bounds.X, bounds.Width); + Span destRow = + destination.GetWritablePixelRowSpanUnsafe(y - bounds.Y).Slice(0, sourceRow.Length); - for (int x = bounds.Left; x < bounds.Right; x++) + for (int x = 0; x < sourceRow.Length; x++) { - TPixel sourcePixel = Unsafe.Add(ref sourceRowRef, x); - Unsafe.Add(ref destinationRowRef, x - offsetX) = quantizer.GetQuantizedColor(sourcePixel, out TPixel transformed); + TPixel sourcePixel = sourceRow[x]; + destRow[x] = quantizer.GetQuantizedColor(sourcePixel, out TPixel transformed); this.Dither(source, bounds, sourcePixel, transformed, x, y, scale); } } } /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyPaletteDither( in TPaletteDitherImageProcessor processor, ImageFrame source, @@ -124,13 +123,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering where TPixel : unmanaged, IPixel { float scale = processor.DitherScale; + for (int y = bounds.Top; y < bounds.Bottom; y++) { - ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); - for (int x = bounds.Left; x < bounds.Right; x++) + Span row = source.GetPixelRowSpan(y).Slice(bounds.X, bounds.Width); + + for (int x = 0; x < row.Length; x++) { - ref TPixel sourcePixel = ref Unsafe.Add(ref sourceRowRef, x); - TPixel transformed = Unsafe.AsRef(processor).GetPaletteColor(sourcePixel); + ref TPixel sourcePixel = ref row[x]; + TPixel transformed = processor.GetPaletteColor(sourcePixel); this.Dither(source, bounds, sourcePixel, transformed, x, y, scale); sourcePixel = transformed; } @@ -138,7 +139,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } // Internal for AOT - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal TPixel Dither( ImageFrame image, Rectangle bounds, diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.KnownTypes.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.KnownTypes.cs index 71ad6db973..1934604522 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.KnownTypes.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.KnownTypes.cs @@ -23,6 +23,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public static OrderedDither Bayer8x8 = new OrderedDither(8); + /// + /// Applies order dithering using the 16x16 Bayer dithering matrix. + /// + public static OrderedDither Bayer16x16 = new OrderedDither(16); + /// /// Applies order dithering using the 3x3 ordered dithering matrix. /// diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs index 9b99a5257d..a89d23cc00 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs @@ -3,7 +3,6 @@ using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -23,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// Initializes a new instance of the struct. /// /// The length of the matrix sides - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public OrderedDither(uint length) { DenseMatrix ditherMatrix = OrderedDitherFactory.CreateDitherMatrix(length); @@ -102,7 +101,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering => !(left == right); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyQuantizationDither( ref TFrameQuantizer quantizer, ImageFrame source, @@ -125,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyPaletteDither( in TPaletteDitherImageProcessor processor, ImageFrame source, @@ -145,24 +144,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering in ditherOperation); } - [MethodImpl(InliningOptions.ShortMethod)] + // Spread assumes an even colorspace distribution and precision. + // Cubed root used because we always compare to Rgb. + // https://bisqwit.iki.fi/story/howto/dither/jy/ + // https://en.wikipedia.org/wiki/Ordered_dithering#Algorithm + internal static int CalculatePaletteSpread(int colors) => (int)(255 / (Math.Pow(colors, 1.0 / 3) - 1)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal TPixel Dither( TPixel source, int x, int y, - int bitDepth, + int spread, float scale) where TPixel : unmanaged, IPixel { - Rgba32 rgba = default; + Unsafe.SkipInit(out Rgba32 rgba); source.ToRgba32(ref rgba); - Rgba32 attempt; + Unsafe.SkipInit(out Rgba32 attempt); - // Spread assumes an even colorspace distribution and precision. - // Calculated as 0-255/component count. 256 / bitDepth - // https://bisqwit.iki.fi/story/howto/dither/jy/ - // https://en.wikipedia.org/wiki/Ordered_dithering#Algorithm - int spread = 256 / bitDepth; float factor = spread * this.thresholdMatrix[y % this.modulusY, x % this.modulusX] * scale; attempt.R = (byte)Numerics.Clamp(rgba.R + factor, byte.MinValue, byte.MaxValue); @@ -181,7 +181,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering => obj is OrderedDither dither && this.Equals(dither); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(OrderedDither other) => this.thresholdMatrix.Equals(other.thresholdMatrix) && this.modulusX == other.modulusX && this.modulusY == other.modulusY; @@ -190,7 +190,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering => this.Equals((object)other); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => HashCode.Combine(this.thresholdMatrix, this.modulusX, this.modulusY); @@ -203,9 +203,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering private readonly ImageFrame source; private readonly IndexedImageFrame destination; private readonly Rectangle bounds; - private readonly int bitDepth; + private readonly int spread; - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public QuantizeDitherRowOperation( ref TFrameQuantizer quantizer, in OrderedDither dither, @@ -218,23 +218,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering this.source = source; this.destination = destination; this.bounds = bounds; - this.bitDepth = ColorNumerics.GetBitsNeededForColorDepth(destination.Palette.Length); + this.spread = CalculatePaletteSpread(destination.Palette.Length); } - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { - int offsetY = this.bounds.Top; - int offsetX = this.bounds.Left; + ref TFrameQuantizer quantizer = ref Unsafe.AsRef(this.quantizer); + int spread = this.spread; float scale = this.quantizer.Options.DitherScale; - ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y)); - ref byte destinationRowRef = ref MemoryMarshal.GetReference(this.destination.GetWritablePixelRowSpanUnsafe(y - offsetY)); + ReadOnlySpan sourceRow = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width); + Span destRow = + this.destination.GetWritablePixelRowSpanUnsafe(y - this.bounds.Y).Slice(0, sourceRow.Length); - for (int x = this.bounds.Left; x < this.bounds.Right; x++) + for (int x = 0; x < sourceRow.Length; x++) { - TPixel dithered = this.dither.Dither(Unsafe.Add(ref sourceRowRef, x), x, y, this.bitDepth, scale); - Unsafe.Add(ref destinationRowRef, x - offsetX) = Unsafe.AsRef(this.quantizer).GetQuantizedColor(dithered, out TPixel _); + TPixel dithered = this.dither.Dither(sourceRow[x], x, y, spread, scale); + destRow[x] = quantizer.GetQuantizedColor(dithered, out TPixel _); } } } @@ -248,9 +249,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering private readonly ImageFrame source; private readonly Rectangle bounds; private readonly float scale; - private readonly int bitDepth; + private readonly int spread; - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public PaletteDitherRowOperation( in TPaletteDitherImageProcessor processor, in OrderedDither dither, @@ -262,19 +263,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering this.source = source; this.bounds = bounds; this.scale = processor.DitherScale; - this.bitDepth = ColorNumerics.GetBitsNeededForColorDepth(processor.Palette.Length); + this.spread = CalculatePaletteSpread(processor.Palette.Length); } - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { - ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y)); + ref TPaletteDitherImageProcessor processor = ref Unsafe.AsRef(this.processor); + int spread = this.spread; + float scale = this.scale; + + Span row = this.source.GetPixelRowSpan(y).Slice(this.bounds.X, this.bounds.Width); - for (int x = this.bounds.Left; x < this.bounds.Right; x++) + for (int x = 0; x < row.Length; x++) { - ref TPixel sourcePixel = ref Unsafe.Add(ref sourceRowRef, x); - TPixel dithered = this.dither.Dither(sourcePixel, x, y, this.bitDepth, this.scale); - sourcePixel = Unsafe.AsRef(this.processor).GetPaletteColor(dithered); + ref TPixel sourcePixel = ref row[x]; + TPixel dithered = this.dither.Dither(sourcePixel, x, y, spread, scale); + sourcePixel = processor.GetPaletteColor(dithered); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index adc3c381a0..5c1b5da7f1 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -37,6 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { KnownDitherings.Bayer2x2, nameof(KnownDitherings.Bayer2x2) }, { KnownDitherings.Bayer4x4, nameof(KnownDitherings.Bayer4x4) }, { KnownDitherings.Bayer8x8, nameof(KnownDitherings.Bayer8x8) }, + { KnownDitherings.Bayer16x16, nameof(KnownDitherings.Bayer16x16) }, { KnownDitherings.Ordered3x3, nameof(KnownDitherings.Ordered3x3) } }; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs index a25eca5b02..3f4656d411 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs @@ -169,8 +169,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization provider.RunRectangleConstrainedValidatingProcessorTest( (x, rect) => x.Quantize(quantizer, rect), - comparer: ValidatorComparer, testOutputDetails: testOutputDetails, + comparer: ValidatorComparer, appendPixelTypeToFileName: false); } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs index 39ebf7f159..5f41021a06 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Tests using (Image image = provider.GetImage()) { FormattableString testOutputDetails = $""; - image.Mutate(ctx => { testOutputDetails = processAndGetTestOutputDetails(ctx); }); + image.Mutate(ctx => testOutputDetails = processAndGetTestOutputDetails(ctx)); image.DebugSave( provider, From a54bce1faa4e7b45717b39e040658a6a543c3ff2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 1 Mar 2021 16:41:10 +0000 Subject: [PATCH 05/25] Update refs. --- .../Processors/Dithering/ErrorDither.cs | 32 +++++++++---------- ...erFilterInBox_Rgba32_CalliphoraPartial.png | 4 +-- ...DependOnSinglePixelType_Bgra32_filter0.png | 4 +-- ...tDependOnSinglePixelType_Rgb24_filter0.png | 4 +-- ...DependOnSinglePixelType_Rgba32_filter0.png | 4 +-- ...ndOnSinglePixelType_RgbaVector_filter0.png | 4 +-- ..._WorksWithAllDitherers_Bike_Bayer16x16.png | 3 ++ ...er_WorksWithAllDitherers_Bike_Bayer2x2.png | 4 +-- ...er_WorksWithAllDitherers_Bike_Bayer4x4.png | 4 +-- ...er_WorksWithAllDitherers_Bike_Bayer8x8.png | 4 +-- ..._WorksWithAllDitherers_Bike_Ordered3x3.png | 4 +-- ...Ditherers_CalliphoraPartial_Bayer16x16.png | 3 ++ ...llDitherers_CalliphoraPartial_Bayer2x2.png | 4 +-- ...llDitherers_CalliphoraPartial_Bayer4x4.png | 4 +-- ...llDitherers_CalliphoraPartial_Bayer8x8.png | 4 +-- ...Ditherers_CalliphoraPartial_Ordered3x3.png | 4 +-- ...Box_Bike_OctreeQuantizer_OrderedDither.png | 4 +-- ..._WebSafePaletteQuantizer_OrderedDither.png | 4 +-- ...e_WernerPaletteQuantizer_OrderedDither.png | 4 +-- ...onInBox_Bike_WuQuantizer_OrderedDither.png | 4 +-- ...aPartial_OctreeQuantizer_OrderedDither.png | 4 +-- ..._WebSafePaletteQuantizer_OrderedDither.png | 4 +-- ...l_WernerPaletteQuantizer_OrderedDither.png | 4 +-- ...phoraPartial_WuQuantizer_OrderedDither.png | 4 +-- ...vid_OctreeQuantizer_OrderedDither_0.25.png | 4 +-- ...avid_OctreeQuantizer_OrderedDither_0.5.png | 4 +-- ...vid_OctreeQuantizer_OrderedDither_0.75.png | 4 +-- ..._david_OctreeQuantizer_OrderedDither_1.png | 4 +-- ...afePaletteQuantizer_OrderedDither_0.25.png | 4 +-- ...SafePaletteQuantizer_OrderedDither_0.5.png | 4 +-- ...afePaletteQuantizer_OrderedDither_0.75.png | 4 +-- ...ebSafePaletteQuantizer_OrderedDither_1.png | 4 +-- ...nerPaletteQuantizer_OrderedDither_0.25.png | 4 +-- ...rnerPaletteQuantizer_OrderedDither_0.5.png | 4 +-- ...nerPaletteQuantizer_OrderedDither_0.75.png | 4 +-- ...WernerPaletteQuantizer_OrderedDither_1.png | 4 +-- ...e_david_WuQuantizer_OrderedDither_0.25.png | 4 +-- ...le_david_WuQuantizer_OrderedDither_0.5.png | 4 +-- ...e_david_WuQuantizer_OrderedDither_0.75.png | 4 +-- ...cale_david_WuQuantizer_OrderedDither_1.png | 4 +-- ...ion_Bike_OctreeQuantizer_OrderedDither.png | 4 +-- ..._WebSafePaletteQuantizer_OrderedDither.png | 4 +-- ...e_WernerPaletteQuantizer_OrderedDither.png | 4 +-- ...ization_Bike_WuQuantizer_OrderedDither.png | 4 +-- ...aPartial_OctreeQuantizer_OrderedDither.png | 4 +-- ..._WebSafePaletteQuantizer_OrderedDither.png | 4 +-- ...l_WernerPaletteQuantizer_OrderedDither.png | 4 +-- ...phoraPartial_WuQuantizer_OrderedDither.png | 4 +-- 48 files changed, 111 insertions(+), 107 deletions(-) create mode 100644 tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer16x16.png create mode 100644 tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer16x16.png diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs index b853fcb669..1a107c2cfd 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs @@ -5,7 +5,6 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -25,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// /// The diffusion matrix. /// The starting offset within the matrix. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public ErrorDither(in DenseMatrix matrix, int offset) { this.matrix = matrix; @@ -87,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering => !(left == right); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void ApplyQuantizationDither( ref TFrameQuantizer quantizer, ImageFrame source, @@ -96,25 +95,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering where TFrameQuantizer : struct, IQuantizer where TPixel : unmanaged, IPixel { + int offsetY = bounds.Top; + int offsetX = bounds.Left; float scale = quantizer.Options.DitherScale; for (int y = bounds.Top; y < bounds.Bottom; y++) { - ReadOnlySpan sourceRow = source.GetPixelRowSpan(y).Slice(bounds.X, bounds.Width); - Span destRow = - destination.GetWritablePixelRowSpanUnsafe(y - bounds.Y).Slice(0, sourceRow.Length); + ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); + ref byte destinationRowRef = ref MemoryMarshal.GetReference(destination.GetWritablePixelRowSpanUnsafe(y - offsetY)); - for (int x = 0; x < sourceRow.Length; x++) + for (int x = bounds.Left; x < bounds.Right; x++) { - TPixel sourcePixel = sourceRow[x]; - destRow[x] = quantizer.GetQuantizedColor(sourcePixel, out TPixel transformed); + TPixel sourcePixel = Unsafe.Add(ref sourceRowRef, x); + Unsafe.Add(ref destinationRowRef, x - offsetX) = quantizer.GetQuantizedColor(sourcePixel, out TPixel transformed); this.Dither(source, bounds, sourcePixel, transformed, x, y, scale); } } } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void ApplyPaletteDither( in TPaletteDitherImageProcessor processor, ImageFrame source, @@ -123,15 +123,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering where TPixel : unmanaged, IPixel { float scale = processor.DitherScale; - for (int y = bounds.Top; y < bounds.Bottom; y++) { - Span row = source.GetPixelRowSpan(y).Slice(bounds.X, bounds.Width); - - for (int x = 0; x < row.Length; x++) + ref TPixel sourceRowRef = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); + for (int x = bounds.Left; x < bounds.Right; x++) { - ref TPixel sourcePixel = ref row[x]; - TPixel transformed = processor.GetPaletteColor(sourcePixel); + ref TPixel sourcePixel = ref Unsafe.Add(ref sourceRowRef, x); + TPixel transformed = Unsafe.AsRef(processor).GetPaletteColor(sourcePixel); this.Dither(source, bounds, sourcePixel, transformed, x, y, scale); sourcePixel = transformed; } @@ -139,7 +137,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } // Internal for AOT - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] internal TPixel Dither( ImageFrame image, Rectangle bounds, diff --git a/tests/Images/External/ReferenceOutput/DitherTests/ApplyDitherFilterInBox_Rgba32_CalliphoraPartial.png b/tests/Images/External/ReferenceOutput/DitherTests/ApplyDitherFilterInBox_Rgba32_CalliphoraPartial.png index 572c719e9e..5059748d2b 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/ApplyDitherFilterInBox_Rgba32_CalliphoraPartial.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/ApplyDitherFilterInBox_Rgba32_CalliphoraPartial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ce9f105031562af60baff3619b9bc84a9f76d77a555a334149d13ddf1e9b44b -size 269077 +oid sha256:d0c8ccdfbf6b1c961f6531ae61207a7f89507f469c875677f1755ea3d6c8d900 +size 326504 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png index b6d77f3f14..0082bae441 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Bgra32_filter0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56b0fc0343c220f672611d1adceffd4f59ba917e7c5766b19568271d0b4a95e0 -size 788 +oid sha256:14231fa7c5c98504277b6452901679027661c5e272106bdcfc516dd519a5ff6c +size 1049 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png index b6d77f3f14..0082bae441 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgb24_filter0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56b0fc0343c220f672611d1adceffd4f59ba917e7c5766b19568271d0b4a95e0 -size 788 +oid sha256:14231fa7c5c98504277b6452901679027661c5e272106bdcfc516dd519a5ff6c +size 1049 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png index b6d77f3f14..208e4fe0ef 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_Rgba32_filter0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56b0fc0343c220f672611d1adceffd4f59ba917e7c5766b19568271d0b4a95e0 -size 788 +oid sha256:d55bf31ae306fcf91993b488444e83ad0f684f4a2642879e38e27e7b9fb1fa56 +size 1051 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png index b6d77f3f14..0082bae441 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_ShouldNotDependOnSinglePixelType_RgbaVector_filter0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56b0fc0343c220f672611d1adceffd4f59ba917e7c5766b19568271d0b4a95e0 -size 788 +oid sha256:14231fa7c5c98504277b6452901679027661c5e272106bdcfc516dd519a5ff6c +size 1049 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer16x16.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer16x16.png new file mode 100644 index 0000000000..6b7ee76a90 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer16x16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:208a0b9a189c8801e97495a93302814679441bbbe1769810eb37bcb52a78518f +size 83344 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer2x2.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer2x2.png index dc944f2123..e91a9551f1 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer2x2.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer2x2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97f4582029805abbae7575df9935a77b55ab7c4f4190f7cfb7a68b3f3dac9cfc -size 39840 +oid sha256:c95ae441b8b090a0c838db5ed3e9b3ae1040225420e79b76c806f88b96716b8f +size 80344 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer4x4.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer4x4.png index 55f36449f2..ffd30f62ce 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer4x4.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer4x4.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:186be7a7c23b9150e669b3aa15e3f7af7e619759ecc4ea2a7be7645c124caebb -size 40570 +oid sha256:e5ab9eb0b80de50f117446c46025918893c431c228e212bef9371f4f788cee14 +size 82652 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer8x8.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer8x8.png index e2c5332cde..e24920a4d5 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer8x8.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Bayer8x8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e038cf3f08e27f7b83921256112378f69cc56880added2fc60bf1f710e2e497e -size 40547 +oid sha256:f76c909b7e804c8dd80b07fd5346d2036d2fded2bf9a855bd20f7da154a111f3 +size 83554 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Ordered3x3.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Ordered3x3.png index 93f286cf29..d70774d3a7 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Ordered3x3.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_Bike_Ordered3x3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f071a490e7a297e7f861a9669afc7641f2fcb264cdef84f562d41fd97ed66504 -size 40751 +oid sha256:656dfb6c9a53830d915a8c8810d09872333a9230073e25b4f0668269afb15e00 +size 83188 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer16x16.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer16x16.png new file mode 100644 index 0000000000..c3eda832a4 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer16x16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b56aa9a03e7f6733fac6b6ceddba50e85727201c4f79aea64540cc79f7fd942e +size 88333 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer2x2.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer2x2.png index 1c6b287913..56660f434b 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer2x2.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer2x2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9994ba45741132a51fab9fdd71e6aaab7ce748c6c393b7515b85bad71878e3ee -size 49984 +oid sha256:863debcf1bc4a4e3fb0e3c29b8b3f8b98bb7ac47901e89a90a57a2dde5d81f53 +size 90431 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer4x4.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer4x4.png index 9647659023..c434e317af 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer4x4.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer4x4.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5360890509baffe70fd84499a0b0e18057f1220660782b1d82e39d36c04dc3f1 -size 49638 +oid sha256:a92785de634c09d73dc91d1a33e52dedd7d5dea79d269753d959f2a1f81afb2b +size 89207 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer8x8.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer8x8.png index 36d8991097..4b04715b9a 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer8x8.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Bayer8x8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e8603ec1daebec049e34658a07bb85333ac996d5d16d64aa318b7f47d966bf6 -size 49253 +oid sha256:e0aeb15a04553142cade051d523bbc18b2e63997efa0b0c5f5b8bab8662074f7 +size 88550 diff --git a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Ordered3x3.png b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Ordered3x3.png index 2d93e79503..fc1e540cc0 100644 --- a/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Ordered3x3.png +++ b/tests/Images/External/ReferenceOutput/DitherTests/DitherFilter_WorksWithAllDitherers_CalliphoraPartial_Ordered3x3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74bdc85112c696133c251d05080c100675cb72f07ed8a8e542faa5255fd0c942 -size 49248 +oid sha256:4801d48fc6691bc2fd555a4bed8a7abdde7edac3dc13b33da580688d11bc4eb4 +size 89543 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_OrderedDither.png index 5880d74893..5d8e6b4565 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_OctreeQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da1630504bf471ef7ee717dd575c9bfaa3cae3839ad9415a5f1abf2c5fa7d418 -size 319991 +oid sha256:adc156f6010679f2ff076405557d0a34cd50464240bbafafbf44edf37b5a1186 +size 321968 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_OrderedDither.png index 50fdf621a8..97613bcaa3 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WebSafePaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b2c154c970f5a80ff3e97f0c0fbe832998e72687be090073416a96d41647141 -size 296210 +oid sha256:ec93dd8fc45e9eb3b1ad13bd89dfc487f5d6eccd2ad8fa1fede67fa7819a263a +size 299393 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_OrderedDither.png index c58ca55215..2ea043d6fe 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WernerPaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e2747cbfc098f4368fd87599b06debbe2a21bcf29260788b868bf9333c9f03c -size 296458 +oid sha256:8f3ef9dab0169bd262408a30ce2a1d20da5acb331fd56ce66de2f7efe4555a9a +size 299734 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_OrderedDither.png index f94978c555..e04186940b 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_Bike_WuQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9adda119665eb27ca1475ce19ab0958b00d6778b9e1733c42cf5e77350f40000 -size 332734 +oid sha256:f3d8d9e978668ae8f76004dc2a8440ffe2f55875ee92046ca2be02f426def1a6 +size 333260 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_OrderedDither.png index c1a78c2ba2..b912690dee 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_OctreeQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7295356c1e0c5f59220bfaf154760e86663709c8e0dbaf156efa13ac45b947a -size 348440 +oid sha256:bdb6866053be7dbe1e56e6972b50bc030d30a050f73a4429993e3c639e06d345 +size 349125 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png index 602699838e..f1b04e74c6 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07b4f6147884f323c99734ddf295d2fc1a072ac95f003d63e217aaa63cdb6b7a -size 325067 +oid sha256:0adaaae399376c94af866adfcb2c5777c0dd91d2d4424f24490909e68d2483c9 +size 326321 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png index f7bc04c741..f54900a2f1 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7a849cce2215bc40da6fd3581e8a7fc87b6ef916e227c622c901985d6ee9b1b -size 333793 +oid sha256:0571bde66f19b41cf1ba6f3b63f3d380a1025ae2f92dda8b9c494f8869c325e4 +size 334758 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_OrderedDither.png index d158820c0f..8165d47763 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationInBox_CalliphoraPartial_WuQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a07a6698cca2e13b674102c92f23b934955406c61c485514d9bbbe62da3e04f5 -size 353191 +oid sha256:8cc6c263430489a8866fab47c26f399a034b0dd583d27b12edc68244919321d0 +size 353592 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.25.png index de86ae9a4b..47552e4571 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.25.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.25.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2fc87ffa7c22b4b054903c6f19bdfcca1d0cdb01df6f853ae4361da3c99f5ab6 -size 34595 +oid sha256:ec01d4ee9173d01f92b5643782f4b6c7e0b4342b530acf6062f5f17c6d7b1e9a +size 36290 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.5.png index a6361a10b2..36e1349ede 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.5.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:243d875938b1a68e6c49a07259a9a155f0d36a2fbdc3350b38e13a3d0c6bff7f -size 36085 +oid sha256:2ed04ff17bc4d7c57a9594bb4872f430cc3df4d92c7199d5c5db2420ecc20a95 +size 38303 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.75.png index d052f58840..760d17d5aa 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.75.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_0.75.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8076572e9e9a391939c166491553421a83aec5085b1bab155fdf7459840054ed -size 37194 +oid sha256:2f8e53d995f27780851c044d552473ee52ec9dcc2e0dfa9a806c9f8d2fd62692 +size 39251 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_1.png index f07eed5711..efaa7bb44b 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_1.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_OctreeQuantizer_OrderedDither_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ad464bf9a0dfbebbc7f3b7a3ff641e2db103456943a8fde5d71d675df66f4c6 -size 37895 +oid sha256:6a7a1cefa7e70387ccb9e90c5633725ce936635da39c131a59cec7089392c358 +size 39744 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.25.png index be7686c0ca..94175f4895 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.25.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.25.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b7369ddaf16e3e13554bf7647096e8947f7ccadadc58646b7c53beee6304f90 -size 17047 +oid sha256:a3b56c451b5e7461782dec2f5dccab18e7ad33efe3d9f1906421c32c75923648 +size 17790 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.5.png index 7878b8c353..c227f65870 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.5.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6e0c73170db2f2d84d4951108b8cdd4967e4272ecadcf5268cd7b34d81df760 -size 17786 +oid sha256:c4d81ab162bd065f438504ea2a44be93cefd7f1b31d7d983e23108e8e19b86fa +size 18390 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.75.png index f7761f6cd0..35e12cf859 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.75.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_0.75.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d54587aab60d4200bb6e01a0ff707230959602b93ff55fbf48c85b61cfb0932a -size 18161 +oid sha256:1d2cb1111d2a3915072ca53404215052bbff42ff9639e8e3c2b4f6a70591fd0e +size 19145 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_1.png index 24414f4d4c..6ff5504ab7 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_1.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WebSafePaletteQuantizer_OrderedDither_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fee85efaf11a2da44933c5ab9e5858558044d1707e40a6fadda6ed5214e40c9c -size 18453 +oid sha256:1133884d19f663d3c643ebe11bdeac65e2ab3d533be43a40b61b3292ea59cd3b +size 19680 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.25.png index 3aecca075b..3b9f8866b4 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.25.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.25.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4947713871d8e832f74f855bc60c3efdacef46fa826a37dfa4ceb2a1e4e23680 -size 21484 +oid sha256:64b29bbd6edca8e444822a97ce9bc674db175c299cbec1cbe596552419f49be7 +size 22239 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.5.png index 78fda73874..1efaf38b6c 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.5.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61b794eb8ff00a1088f4584c98066fd5e309c1711f2a430ae24fc482524e6573 -size 22374 +oid sha256:ae09ad6a81dbfc56c60b7e47720338b3ba3b8aa29982016c36a39baa33f75054 +size 23353 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.75.png index 21f8d67744..ed9531e7bb 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.75.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_0.75.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe1e3eae55fad479016d869a3b483155f3be947636da96d105e16072fbb4e350 -size 22968 +oid sha256:e9e9094177282dd635a02b97855299e9275af364fd66812dd72b3ef2545b5660 +size 24487 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_1.png index 9c90d31b0c..22b642dffa 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_1.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WernerPaletteQuantizer_OrderedDither_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2df6b0869c9cb406417bca7eedd2b05968a5265b933dfbec87fff018c7b83ce9 -size 23395 +oid sha256:874ffc514300dd727c6c46943fc9f8955013c1d355fc1bd60848660ed9b4f6b2 +size 25182 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.25.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.25.png index 5a2dc3f01d..39820f08c6 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.25.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.25.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b984d1f65c4c9b933b7f30c06c7f036f77463c9df092165145457f607c7e8ef -size 27395 +oid sha256:46989a5fd14a9555eee28081ad78c34e26f5c38e6d7360cb36de8a87d2916685 +size 29187 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.5.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.5.png index 780703f694..e152e9c48e 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.5.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ae97e27986c742e451b490e28415d3083bd2cc7bc581852d846f55030f1bd87 -size 28778 +oid sha256:c73448e92f13c979c3a0c4f16532a6f47a14e6e1974d686674862070787b6489 +size 31145 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.75.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.75.png index fcdaf069f4..f37e332f37 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.75.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_0.75.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e92f0af9f0de526e6f9da454b16a0dfa164e04ef48ac1b6b0b4958c28821d6b1 -size 30124 +oid sha256:afc516374154a209a07f069eb7832808eefc0db4f2a3fbfa765848ca0d7acedf +size 31974 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_1.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_1.png index 35feecd3a6..e4b8623075 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_1.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantizationWithDitheringScale_david_WuQuantizer_OrderedDither_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c40b16fcfb88c87aaa8be255257703edbcbe82cf33a1ef107b9076deb62f4d02 -size 30663 +oid sha256:be814de172c0b290e4af81ea175e14643e9dc34ce3400ae1f3b64228e29bf49d +size 32237 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_OrderedDither.png index 296b3f2c71..302188cf58 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_OctreeQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c16042705b2cdf610a8a70b9932b3410df7266ec63497e947f99ee049d95d55 -size 134155 +oid sha256:3528fe676ae29534d80edcd08ca5874bcaaae6c1133332070dcd008df2c50da7 +size 138694 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_OrderedDither.png index a83e7367f8..e24920a4d5 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WebSafePaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8586c2d716e1a4b8309ee8010eed5ad6abeaef957eba616466df86155246cea6 -size 75964 +oid sha256:f76c909b7e804c8dd80b07fd5346d2036d2fded2bf9a855bd20f7da154a111f3 +size 83554 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_OrderedDither.png index 087c28243a..32475f3874 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WernerPaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2facff0487dafe3de174758f7e1c237e7d6405fb5efd087a25ea502eaa94ea82 -size 71959 +oid sha256:22cebdb64f32d4818a35c07a4a2f5c2b1bae1fd465944d553b37a211f3e78ff8 +size 79480 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_OrderedDither.png index c07774ad69..74c5cb62da 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_Bike_WuQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa01ed20182ea419ceee6ac19a4044a9aa31e33c797b87c07f6673816f9c6bf8 -size 172305 +oid sha256:385063f3976342ea525487e53801df14e644eb0a56898b1e81e0667323ff3f1a +size 172869 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_OrderedDither.png index feda429b1c..2d16e4af11 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_OctreeQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b0a1b167065207ae11637d1d3b3e89069aa2f7c5f5d37d3974f84eef4b06cb4 -size 141157 +oid sha256:27e0be11cb36a419a590de19cce432f5b78d9a3c86d024ca43b5904e758c569d +size 144104 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png index bf33383b26..4b04715b9a 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WebSafePaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:300f04232c77cf6dce492d6782fac4b96fc868819a8b6125d29d41a07dcecbb3 -size 85437 +oid sha256:e0aeb15a04553142cade051d523bbc18b2e63997efa0b0c5f5b8bab8662074f7 +size 88550 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png index c123626a87..07003dfa4e 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WernerPaletteQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d57dbc32780b6c9bedf6a6c3d275f5c4d960c295eee4e731631a0f88ec2109b0 -size 108975 +oid sha256:e720cb4ab955614764cc0c10f08146a50e08d4c5712a02b581ae25a4e4935c3a +size 113199 diff --git a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_OrderedDither.png b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_OrderedDither.png index cfbaff51e7..9f04685444 100644 --- a/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_OrderedDither.png +++ b/tests/Images/External/ReferenceOutput/QuantizerTests/ApplyQuantization_CalliphoraPartial_WuQuantizer_OrderedDither.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88c97f88cc6c48a448ee77c0fbdcba29d8f091978fe8906fa1aaf1265de13ac6 -size 173601 +oid sha256:462a0d7d7d8056042e49dff3a896114d7db09b9e40e72e6b87f711caf6c1a993 +size 175519 From 5f4a7accfb15db86e5ed8ec0c4e68ef19b654184 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 2 Mar 2021 15:58:24 +0000 Subject: [PATCH 06/25] Update OrderedDither.cs --- .../Processors/Dithering/OrderedDither.cs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs index a89d23cc00..2b7eb165eb 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// Initializes a new instance of the struct. /// /// The length of the matrix sides - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public OrderedDither(uint length) { DenseMatrix ditherMatrix = OrderedDitherFactory.CreateDitherMatrix(length); @@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering => !(left == right); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void ApplyQuantizationDither( ref TFrameQuantizer quantizer, ImageFrame source, @@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void ApplyPaletteDither( in TPaletteDitherImageProcessor processor, ImageFrame source, @@ -145,12 +145,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } // Spread assumes an even colorspace distribution and precision. - // Cubed root used because we always compare to Rgb. + // TODO: Cubed root is currently used to represent 3 color channels + // but we should introduce something to PixelTypeInfo. // https://bisqwit.iki.fi/story/howto/dither/jy/ // https://en.wikipedia.org/wiki/Ordered_dithering#Algorithm - internal static int CalculatePaletteSpread(int colors) => (int)(255 / (Math.Pow(colors, 1.0 / 3) - 1)); + internal static int CalculatePaletteSpread(int colors) + => (int)(255 / Math.Max(1, Math.Pow(colors, 1.0 / 3) - 1)); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] internal TPixel Dither( TPixel source, int x, @@ -181,7 +183,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering => obj is OrderedDither dither && this.Equals(dither); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(OrderedDither other) => this.thresholdMatrix.Equals(other.thresholdMatrix) && this.modulusX == other.modulusX && this.modulusY == other.modulusY; @@ -190,7 +192,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering => this.Equals((object)other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() => HashCode.Combine(this.thresholdMatrix, this.modulusX, this.modulusY); @@ -205,7 +207,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering private readonly Rectangle bounds; private readonly int spread; - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public QuantizeDitherRowOperation( ref TFrameQuantizer quantizer, in OrderedDither dither, @@ -221,7 +223,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering this.spread = CalculatePaletteSpread(destination.Palette.Length); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y) { ref TFrameQuantizer quantizer = ref Unsafe.AsRef(this.quantizer); @@ -251,7 +253,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering private readonly float scale; private readonly int spread; - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public PaletteDitherRowOperation( in TPaletteDitherImageProcessor processor, in OrderedDither dither, @@ -266,7 +268,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering this.spread = CalculatePaletteSpread(processor.Palette.Length); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y) { ref TPaletteDitherImageProcessor processor = ref Unsafe.AsRef(this.processor); From 58c5bf18dc12afd2cd9dc92d921d36125ef2614b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 2 Mar 2021 16:35:12 +0000 Subject: [PATCH 07/25] Bump Magick.NET --- tests/Directory.Build.targets | 2 +- .../TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index 9495b912b6..af86f49b09 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -21,7 +21,7 @@ - + diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index d20e7d2a7e..163bb84d56 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -7,7 +7,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using ImageMagick; -using ImageMagick.Formats.Bmp; +using ImageMagick.Formats; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; From d1c2172406783ed88b8c1f0358837719f932fd64 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 9 Mar 2021 12:07:47 +0100 Subject: [PATCH 08/25] Image.Load(byte[]) should return non-generic Image --- src/ImageSharp/Image.FromBytes.cs | 4 ++-- tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 50950c192a..b8b4f056d9 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -92,8 +92,8 @@ namespace SixLabors.ImageSharp /// The configuration is null. /// The data is null. /// A new . - public static Image Load(byte[] data) - => Load(Configuration.Default, data); + public static Image Load(byte[] data) + => Load(Configuration.Default, data); /// /// Load a new instance of from the given encoded byte array. diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 058d669d40..446f1e9d47 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -155,9 +155,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif [Fact] public void CanDecodeIntermingledImages() { - using (var kumin1 = Image.Load(TestFile.Create(TestImages.Gif.Kumin).Bytes)) + using (var kumin1 = Image.Load(TestFile.Create(TestImages.Gif.Kumin).Bytes)) using (Image.Load(TestFile.Create(TestImages.Png.Icon).Bytes)) - using (var kumin2 = Image.Load(TestFile.Create(TestImages.Gif.Kumin).Bytes)) + using (var kumin2 = Image.Load(TestFile.Create(TestImages.Gif.Kumin).Bytes)) { for (int i = 0; i < kumin1.Frames.Count; i++) { From 583bb6bd6d2a016f0f525f23ba830f1ee2d191cf Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 9 Mar 2021 12:14:09 +0100 Subject: [PATCH 09/25] Also fix "" --- src/ImageSharp/Image.FromBytes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index b8b4f056d9..a33a345a0f 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp /// The byte array containing image data. /// The configuration is null. /// The data is null. - /// A new . + /// A new . public static Image Load(byte[] data) => Load(Configuration.Default, data); From 10739590ce81ed37e63dd05dabbff7e96ba48944 Mon Sep 17 00:00:00 2001 From: UltraNamahage <60680748+UltraNamahage@users.noreply.github.com> Date: Wed, 10 Mar 2021 18:46:49 +0900 Subject: [PATCH 10/25] Have AotCompilerTools cache additional methods. --- src/ImageSharp/Advanced/AotCompilerTools.cs | 348 ++++++++++++++++++++ 1 file changed, 348 insertions(+) diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index ed23cd445a..666b459e42 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -7,10 +7,25 @@ using System.IO; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Processing.Processors.Binarization; +using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.ImageSharp.Processing.Processors.Dithering; +using SixLabors.ImageSharp.Processing.Processors.Drawing; +using SixLabors.ImageSharp.Processing.Processors.Effects; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using SixLabors.ImageSharp.Processing.Processors.Normalization; +using SixLabors.ImageSharp.Processing.Processors.Overlays; using SixLabors.ImageSharp.Processing.Processors.Quantization; +using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Advanced { @@ -89,6 +104,18 @@ namespace SixLabors.ImageSharp.Advanced AotCompileDithering(); AotCompilePixelOperations(); + AotCompileImage(); + AotCompileImageProcessingContextFactory(); + AotCompileImageEncoderInternals(); + AotCompileImageDecoderInternals(); + AotCompileImageEncoders(); + AotCompileImageDecoders(); + AotCompileImageProcessors(); + AotCompileGenericImageProcessors(); + AotCompileResamplers(); + AotCompileQuantizers(); + AotCompilePixelSamplingStrategys(); + Unsafe.SizeOf(); AotCodecs(); @@ -213,5 +240,326 @@ namespace SixLabors.ImageSharp.Advanced var pixelOp = new PixelOperations(); pixelOp.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Clear); } + + /// + /// This method pre-seeds the for a given pixel format in the AoT compiler. + /// + /// The pixel format. + private static unsafe void AotCompileImage() + where TPixel : unmanaged, IPixel + { + Image img = default; + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + + ImageFrame.LoadPixelData(default, default(ReadOnlySpan), default, default); + ImageFrame.LoadPixelData(default, default(ReadOnlySpan), default, default); + } + + private static void AotCompileImageProcessingContextFactory() + where TPixel : unmanaged, IPixel + => default(DefaultImageOperationsProviderFactory).CreateImageProcessingContext(default, default, default); + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + private static void AotCompileImageEncoderInternals() + where TPixel : unmanaged, IPixel + { + default(BmpEncoderCore).Encode(default, default, default); + default(GifEncoderCore).Encode(default, default, default); + default(JpegEncoderCore).Encode(default, default, default); + default(PngEncoderCore).Encode(default, default, default); + default(TgaEncoderCore).Encode(default, default, default); + } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + private static void AotCompileImageDecoderInternals() + where TPixel : unmanaged, IPixel + { + default(BmpDecoderCore).Decode(default, default, default); + default(GifDecoderCore).Decode(default, default, default); + default(JpegDecoderCore).Decode(default, default, default); + default(PngDecoderCore).Decode(default, default, default); + default(TgaDecoderCore).Decode(default, default, default); + } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + private static void AotCompileImageEncoders() + where TPixel : unmanaged, IPixel + { + AotCompileImageEncoder(); + AotCompileImageEncoder(); + AotCompileImageEncoder(); + AotCompileImageEncoder(); + AotCompileImageEncoder(); + } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + private static void AotCompileImageDecoders() + where TPixel : unmanaged, IPixel + { + AotCompileImageDecoder(); + AotCompileImageDecoder(); + AotCompileImageDecoder(); + AotCompileImageDecoder(); + AotCompileImageDecoder(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The encoder. + private static void AotCompileImageEncoder() + where TPixel : unmanaged, IPixel + where TEncoder : class, IImageEncoder + { + default(TEncoder).Encode(default, default); + default(TEncoder).EncodeAsync(default, default, default); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The decoder. + private static void AotCompileImageDecoder() + where TPixel : unmanaged, IPixel + where TDecoder : class, IImageDecoder + { + default(TDecoder).Decode(default, default); + default(TDecoder).DecodeAsync(default, default, default); + } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// + /// There is no structure that implements ISwizzler. + /// + /// The pixel format. + private static void AotCompileImageProcessors() + where TPixel : unmanaged, IPixel + { + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + + AotCompilerCloningImageProcessor(); + AotCompilerCloningImageProcessor(); + AotCompilerCloningImageProcessor(); + AotCompilerCloningImageProcessor(); + AotCompilerCloningImageProcessor(); + AotCompilerCloningImageProcessor(); + AotCompilerCloningImageProcessor(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The processor type + private static void AotCompileImageProcessor() + where TPixel : unmanaged, IPixel + where TProc : class, IImageProcessor + => default(TProc).CreatePixelSpecificProcessor(default, default, default); + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The processor type + private static void AotCompilerCloningImageProcessor() + where TPixel : unmanaged, IPixel + where TProc : class, ICloningImageProcessor + => default(TProc).CreatePixelSpecificCloningProcessor(default, default, default); + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// + /// There is no structure that implements ISwizzler. + /// + /// The pixel format. + private static void AotCompileGenericImageProcessors() + where TPixel : unmanaged, IPixel + { + AotCompileGenericCloningImageProcessor>(); + AotCompileGenericCloningImageProcessor>(); + AotCompileGenericCloningImageProcessor>(); + AotCompileGenericCloningImageProcessor>(); + AotCompileGenericCloningImageProcessor>(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The processor type + private static void AotCompileGenericCloningImageProcessor() + where TPixel : unmanaged, IPixel + where TProc : class, ICloningImageProcessor + => default(TProc).CloneAndExecute(); + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + private static void AotCompileResamplers() + where TPixel : unmanaged, IPixel + { + AotCompileResampler(); + AotCompileResampler(); + AotCompileResampler(); + AotCompileResampler(); + AotCompileResampler(); + AotCompileResampler(); + AotCompileResampler(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The processor type + private static void AotCompileResampler() + where TPixel : unmanaged, IPixel + where TResampler : struct, IResampler + { + default(TResampler).ApplyTransform(default); + + default(AffineTransformProcessor).ApplyTransform(default); + default(ProjectiveTransformProcessor).ApplyTransform(default); + default(ResizeProcessor).ApplyTransform(default); + default(RotateProcessor).ApplyTransform(default); + } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + private static void AotCompileQuantizers() + where TPixel : unmanaged, IPixel + { + AotCompileQuantizer(); + AotCompileQuantizer(); + AotCompileQuantizer(); + AotCompileQuantizer(); + AotCompileQuantizer(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The quantizer type + private static void AotCompileQuantizer() + where TPixel : unmanaged, IPixel + + where TQuantizer : class, IQuantizer + { + default(TQuantizer).CreatePixelSpecificQuantizer(default); + default(TQuantizer).CreatePixelSpecificQuantizer(default, default); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + private static void AotCompilePixelSamplingStrategys() + where TPixel : unmanaged, IPixel + { + default(DefaultPixelSamplingStrategy).EnumeratePixelRegions(default); + default(ExtensivePixelSamplingStrategy).EnumeratePixelRegions(default); + } } } From ece070705369bd3bc8329813aef57d0d1d2edaf9 Mon Sep 17 00:00:00 2001 From: UltraNamahage <60680748+UltraNamahage@users.noreply.github.com> Date: Sun, 14 Mar 2021 04:10:01 +0900 Subject: [PATCH 11/25] Organize Seed and solve the problem of more Seed and code strips with the Preserve attribute. --- src/ImageSharp/Advanced/AotCompilerTools.cs | 320 +++++++++--------- src/ImageSharp/Advanced/PreserveAttribute.cs | 14 + .../PaletteDitherProcessor{TPixel}.cs | 3 +- 3 files changed, 168 insertions(+), 169 deletions(-) create mode 100644 src/ImageSharp/Advanced/PreserveAttribute.cs diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 666b459e42..ea4cd1c8c4 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats; @@ -13,6 +12,7 @@ using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors; @@ -40,70 +40,82 @@ namespace SixLabors.ImageSharp.Advanced [ExcludeFromCodeCoverage] internal static class AotCompilerTools { - static AotCompilerTools() - { - Unsafe.SizeOf(); - Unsafe.SizeOf(); - Unsafe.SizeOf(); - Unsafe.SizeOf(); - Unsafe.SizeOf(); - Unsafe.SizeOf(); - Unsafe.SizeOf(); - Unsafe.SizeOf(); - } - /// /// This is the method that seeds the AoT compiler. /// None of these seed methods needs to actually be called to seed the compiler. /// The calls just need to be present when the code is compiled, and each implementation will be built. /// + /// + /// This method doesn't actually do anything but serves an important purpose... + /// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an exception: + /// "Attempting to JIT compile method... OctreeFrameQuantizer.ConstructPalette... while running in aot-only mode." + /// The reason this happens is the SaveAsGif method makes heavy use of generics, which are too confusing for the AoT + /// compiler used on Xamarin.iOS. It spins up the JIT compiler to try and figure it out, but that is an illegal op on + /// iOS so it bombs out. + /// If you are getting the above error, you need to call this method, which will pre-seed the AoT compiler with the + /// necessary methods to complete the SaveAsGif call. That's it, otherwise you should NEVER need this method!!! + /// + [Preserve] private static void SeedEverything() { - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); + try + { + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + } + catch + { + // nop + } + + throw new InvalidOperationException("This method is used for AOT code generation only. Do not call it at runtime."); } /// /// Seeds the compiler using the given pixel format. /// /// The pixel format. + [Preserve] private static void Seed() where TPixel : unmanaged, IPixel { // This is we actually call all the individual methods you need to seed. - AotPixelInterface(); - AotCompileOctreeQuantizer(); - AotCompileWuQuantizer(); - AotCompilePaletteQuantizer(); - AotCompileDithering(); - AotCompilePixelOperations(); - AotCompileImage(); AotCompileImageProcessingContextFactory(); AotCompileImageEncoderInternals(); @@ -115,136 +127,19 @@ namespace SixLabors.ImageSharp.Advanced AotCompileResamplers(); AotCompileQuantizers(); AotCompilePixelSamplingStrategys(); + AotCompileDithers(); + AotCompileMemoryManagers(); Unsafe.SizeOf(); - AotCodecs(); - // TODO: Do the discovery work to figure out what works and what doesn't. } - private static void AotPixelInterface() - where TPixel : unmanaged, IPixel - { - TPixel pixel = default; - Rgba32 rgba32 = default; - pixel.ToRgba32(ref rgba32); - pixel.FromRgba32(rgba32); - pixel.FromScaledVector4(pixel.ToScaledVector4()); - pixel.FromVector4(pixel.ToVector4()); - - pixel.FromArgb32(default); - pixel.FromBgr24(default); - pixel.FromBgra32(default); - pixel.FromBgra5551(default); - pixel.FromL16(default); - pixel.FromL8(default); - pixel.FromLa16(default); - pixel.FromLa32(default); - pixel.FromRgb24(default); - pixel.FromRgb48(default); - pixel.FromRgba64(default); - pixel.FromRgb24(default); - } - - /// - /// This method doesn't actually do anything but serves an important purpose... - /// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an exception: - /// "Attempting to JIT compile method... OctreeFrameQuantizer.ConstructPalette... while running in aot-only mode." - /// The reason this happens is the SaveAsGif method makes heavy use of generics, which are too confusing for the AoT - /// compiler used on Xamarin.iOS. It spins up the JIT compiler to try and figure it out, but that is an illegal op on - /// iOS so it bombs out. - /// If you are getting the above error, you need to call this method, which will pre-seed the AoT compiler with the - /// necessary methods to complete the SaveAsGif call. That's it, otherwise you should NEVER need this method!!! - /// - /// The pixel format. - private static void AotCompileOctreeQuantizer() - where TPixel : unmanaged, IPixel - { - using var test = new OctreeQuantizer(Configuration.Default, new OctreeQuantizer().Options); - var frame = new ImageFrame(Configuration.Default, 1, 1); - test.QuantizeFrame(frame, frame.Bounds()); - } - - /// - /// This method pre-seeds the WuQuantizer in the AoT compiler for iOS. - /// - /// The pixel format. - private static void AotCompileWuQuantizer() - where TPixel : unmanaged, IPixel - { - using var test = new WuQuantizer(Configuration.Default, new WuQuantizer().Options); - var frame = new ImageFrame(Configuration.Default, 1, 1); - test.QuantizeFrame(frame, frame.Bounds()); - } - - /// - /// This method pre-seeds the PaletteQuantizer in the AoT compiler for iOS. - /// - /// The pixel format. - private static void AotCompilePaletteQuantizer() - where TPixel : unmanaged, IPixel - { - using var test = (PaletteQuantizer)new PaletteQuantizer(Array.Empty()).CreatePixelSpecificQuantizer(Configuration.Default); - var frame = new ImageFrame(Configuration.Default, 1, 1); - test.QuantizeFrame(frame, frame.Bounds()); - } - - /// - /// This method pre-seeds the default dithering engine (FloydSteinbergDiffuser) in the AoT compiler for iOS. - /// - /// The pixel format. - private static void AotCompileDithering() - where TPixel : unmanaged, IPixel - { - ErrorDither errorDither = ErrorDither.FloydSteinberg; - OrderedDither orderedDither = OrderedDither.Bayer2x2; - TPixel pixel = default; - using (var image = new ImageFrame(Configuration.Default, 1, 1)) - { - errorDither.Dither(image, image.Bounds(), pixel, pixel, 0, 0, 0); - orderedDither.Dither(pixel, 0, 0, 0, 0); - } - } - - /// - /// This method pre-seeds the decoder and encoder for a given pixel format in the AoT compiler for iOS. - /// - /// The pixel format. - private static void AotCodecs() - where TPixel : unmanaged, IPixel - { - Configuration configuration = Configuration.Default; - ImageFormatManager formatsManager = configuration.ImageFormatsManager; - foreach (IImageFormat imageFormat in configuration.ImageFormats) - { - using var ms = new MemoryStream(); - using (var encoded = new Image(1, 1)) - { - encoded.Save(ms, formatsManager.FindEncoder(imageFormat)); - ms.Position = 0; - } - - using var decoded = Image.Load(ms); - Span span = decoded.GetPixelRowSpan(0); - } - } - - /// - /// This method pre-seeds the PixelOperations engine for the AoT compiler on iOS. - /// - /// The pixel format. - private static void AotCompilePixelOperations() - where TPixel : unmanaged, IPixel - { - var pixelOp = new PixelOperations(); - pixelOp.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Clear); - } - /// /// This method pre-seeds the for a given pixel format in the AoT compiler. /// /// The pixel format. + [Preserve] private static unsafe void AotCompileImage() where TPixel : unmanaged, IPixel { @@ -282,6 +177,11 @@ namespace SixLabors.ImageSharp.Advanced ImageFrame.LoadPixelData(default, default(ReadOnlySpan), default, default); } + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + [Preserve] private static void AotCompileImageProcessingContextFactory() where TPixel : unmanaged, IPixel => default(DefaultImageOperationsProviderFactory).CreateImageProcessingContext(default, default, default); @@ -290,6 +190,7 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompileImageEncoderInternals() where TPixel : unmanaged, IPixel { @@ -304,6 +205,7 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompileImageDecoderInternals() where TPixel : unmanaged, IPixel { @@ -318,6 +220,7 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompileImageEncoders() where TPixel : unmanaged, IPixel { @@ -332,6 +235,7 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompileImageDecoders() where TPixel : unmanaged, IPixel { @@ -347,6 +251,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The encoder. + [Preserve] private static void AotCompileImageEncoder() where TPixel : unmanaged, IPixel where TEncoder : class, IImageEncoder @@ -360,6 +265,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The decoder. + [Preserve] private static void AotCompileImageDecoder() where TPixel : unmanaged, IPixel where TDecoder : class, IImageDecoder @@ -375,6 +281,7 @@ namespace SixLabors.ImageSharp.Advanced /// There is no structure that implements ISwizzler. /// /// The pixel format. + [Preserve] private static void AotCompileImageProcessors() where TPixel : unmanaged, IPixel { @@ -448,6 +355,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The processor type + [Preserve] private static void AotCompileImageProcessor() where TPixel : unmanaged, IPixel where TProc : class, IImageProcessor @@ -458,6 +366,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The processor type + [Preserve] private static void AotCompilerCloningImageProcessor() where TPixel : unmanaged, IPixel where TProc : class, ICloningImageProcessor @@ -470,6 +379,7 @@ namespace SixLabors.ImageSharp.Advanced /// There is no structure that implements ISwizzler. /// /// The pixel format. + [Preserve] private static void AotCompileGenericImageProcessors() where TPixel : unmanaged, IPixel { @@ -485,6 +395,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The processor type + [Preserve] private static void AotCompileGenericCloningImageProcessor() where TPixel : unmanaged, IPixel where TProc : class, ICloningImageProcessor @@ -494,6 +405,7 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompileResamplers() where TPixel : unmanaged, IPixel { @@ -511,6 +423,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The processor type + [Preserve] private static void AotCompileResampler() where TPixel : unmanaged, IPixel where TResampler : struct, IResampler @@ -527,6 +440,7 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompileQuantizers() where TPixel : unmanaged, IPixel { @@ -542,6 +456,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The pixel format. /// The quantizer type + [Preserve] private static void AotCompileQuantizer() where TPixel : unmanaged, IPixel @@ -555,11 +470,80 @@ namespace SixLabors.ImageSharp.Advanced /// This method pre-seeds the in the AoT compiler. /// /// The pixel format. + [Preserve] private static void AotCompilePixelSamplingStrategys() where TPixel : unmanaged, IPixel { default(DefaultPixelSamplingStrategy).EnumeratePixelRegions(default); default(ExtensivePixelSamplingStrategy).EnumeratePixelRegions(default); } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + [Preserve] + private static void AotCompileDithers() + where TPixel : unmanaged, IPixel + { + AotCompileDither(); + AotCompileDither(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The dither. + [Preserve] + private static void AotCompileDither() + where TPixel : unmanaged, IPixel + where TDither : struct, IDither + { + var octree = default(OctreeQuantizer); + default(TDither).ApplyQuantizationDither, TPixel>(ref octree, default, default, default); + + var palette = default(PaletteQuantizer); + default(TDither).ApplyQuantizationDither, TPixel>(ref palette, default, default, default); + + var wu = default(WuQuantizer); + default(TDither).ApplyQuantizationDither, TPixel>(ref wu, default, default, default); + default(TDither).ApplyPaletteDither.DitherProcessor, TPixel>(default, default, default); + } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + [Preserve] + private static void AotCompileMemoryManagers() + where TPixel : unmanaged, IPixel + { + AotCompileMemoryManager(); + AotCompileMemoryManager(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The buffer. + [Preserve] + private static void AotCompileMemoryManager() + where TPixel : unmanaged, IPixel + where TBuffer : MemoryAllocator + { + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + } } } diff --git a/src/ImageSharp/Advanced/PreserveAttribute.cs b/src/ImageSharp/Advanced/PreserveAttribute.cs new file mode 100644 index 0000000000..a16b30e235 --- /dev/null +++ b/src/ImageSharp/Advanced/PreserveAttribute.cs @@ -0,0 +1,14 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// This is necessary to avoid being excluded from compilation in environments that do AOT builds, such as Unity's IL2CPP and Xamarin. + /// The only thing that matters is the class name. + /// There is no need to use or inherit from the PreserveAttribute class in each environment. + /// + internal sealed class PreserveAttribute : System.Attribute + { + } +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs index 789d02046e..4631cd4229 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs @@ -72,7 +72,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// Used to allow inlining of calls to /// . /// - private readonly struct DitherProcessor : IPaletteDitherImageProcessor + /// Internal for AOT + internal readonly struct DitherProcessor : IPaletteDitherImageProcessor { private readonly EuclideanPixelMap pixelMap; From 7eaae92bbf5be2da810dde915681e3aff41b9fab Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Sun, 28 Mar 2021 22:00:45 +0200 Subject: [PATCH 12/25] Grayscale Jpeg encoding --- .../Jpeg/Components/Encoder/L8ToYConverter.cs | 49 +++++ .../LuminanceForwardConverter{TPixel}.cs | 59 ++++++ .../Formats/Jpeg/IJpegEncoderOptions.cs | 9 +- src/ImageSharp/Formats/Jpeg/JpegColorType.cs | 21 ++ src/ImageSharp/Formats/Jpeg/JpegEncoder.cs | 20 ++ .../Formats/Jpeg/JpegEncoderCore.cs | 188 ++++++++++++------ src/ImageSharp/Formats/Jpeg/JpegSubsample.cs | 7 +- .../Formats/Jpg/JpegEncoderTests.cs | 12 +- 8 files changed, 298 insertions(+), 67 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/Components/Encoder/L8ToYConverter.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs create mode 100644 src/ImageSharp/Formats/Jpeg/JpegColorType.cs diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/L8ToYConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/L8ToYConverter.cs new file mode 100644 index 0000000000..6d787c58ff --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/L8ToYConverter.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder +{ + /// + /// Provides 8-bit lookup tables for converting from L8 to Y colorspace. + /// + internal unsafe struct L8ToYConverter + { + /// + /// Initializes + /// + /// The initialized + public static L8ToYConverter Create() + { + L8ToYConverter converter = default; + return converter; + } + + /// + /// Optimized method to allocates the correct y, cb, and cr values to the DCT blocks from the given r, g, b values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ConvertPixelInto( + int l, + ref Block8x8F yResult, + int i) => yResult[i] = l; + + public void Convert(Span l8Span, ref Block8x8F yBlock) + { + ref L8 l8Start = ref l8Span[0]; + + for (int i = 0; i < 64; i++) + { + ref L8 c = ref Unsafe.Add(ref l8Start, i); + + this.ConvertPixelInto( + c.PackedValue, + ref yBlock, + i); + } + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs new file mode 100644 index 0000000000..0b6cde8269 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs @@ -0,0 +1,59 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder +{ + /// + /// On-stack worker struct to efficiently encapsulate the TPixel -> L8 -> Y conversion chain of 8x8 pixel blocks. + /// + /// The pixel type to work on + internal ref struct LuminanceForwardConverter + where TPixel : unmanaged, IPixel + { + /// + /// The Y component + /// + public Block8x8F Y; + + /// + /// The converter + /// + private L8ToYConverter converter; + + /// + /// Temporal 8x8 block to hold TPixel data + /// + private GenericBlock8x8 pixelBlock; + + /// + /// Temporal RGB block + /// + private GenericBlock8x8 l8Block; + + public static LuminanceForwardConverter Create() + { + var result = default(LuminanceForwardConverter); + result.converter = L8ToYConverter.Create(); + return result; + } + + /// + /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure () + /// + public void Convert(ImageFrame frame, int x, int y, ref RowOctet currentRows) + { + this.pixelBlock.LoadAndStretchEdges(frame.PixelBuffer, x, y, ref currentRows); + + Span l8Span = this.l8Block.AsSpanUnsafe(); + PixelOperations.Instance.ToL8(frame.GetConfiguration(), this.pixelBlock.AsSpanUnsafe(), l8Span); + + ref Block8x8F yBlock = ref this.Y; + + this.converter.Convert(l8Span, ref yBlock); + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs b/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs index ecd64a7823..cceed407c2 100644 --- a/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs +++ b/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Jpeg @@ -20,5 +20,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// The subsample ratio of the jpg image. JpegSubsample? Subsample { get; } + + /// + /// Gets the color type. + /// + JpegColorType? ColorType { get; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/JpegColorType.cs b/src/ImageSharp/Formats/Jpeg/JpegColorType.cs new file mode 100644 index 0000000000..73b3215d62 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/JpegColorType.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Jpeg +{ + /// + /// Provides enumeration of available JPEG color types. + /// + public enum JpegColorType : byte + { + /// + /// YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification. + /// + YCbCr = 0, + + /// + /// Single channel, luminance. + /// + Luminance = 1 + } +} diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index b549bd8a32..6842cfe15a 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -25,6 +25,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public JpegSubsample? Subsample { get; set; } + /// + /// Gets or sets the color type, that will be used to encode the image. + /// + public JpegColorType? ColorType { get; set; } + /// /// Encodes the image to the specified stream from the . /// @@ -35,6 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg where TPixel : unmanaged, IPixel { var encoder = new JpegEncoderCore(this); + this.EnrichColorType(); encoder.Encode(image, stream); } @@ -50,7 +56,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg where TPixel : unmanaged, IPixel { var encoder = new JpegEncoderCore(this); + this.EnrichColorType(); return encoder.EncodeAsync(image, stream, cancellationToken); } + + /// + /// If ColorType was not set, set it based on the given . + /// + private void EnrichColorType() + where TPixel : unmanaged, IPixel + { + if (this.ColorType == null) + { + bool isGrayscale = typeof(TPixel) == typeof(L8) || typeof(TPixel) == typeof(L16); + this.ColorType = isGrayscale ? JpegColorType.Luminance : JpegColorType.YCbCr; + } + } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index d26fbb936d..bca03b109b 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -57,6 +57,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// private readonly int? quality; + /// + /// Gets or sets the subsampling method to use. + /// + private readonly JpegColorType? colorType; + /// /// The accumulated bits to write to the stream. /// @@ -90,6 +95,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { this.quality = options.Quality; this.subsample = options.Subsample; + this.colorType = options.ColorType; } /// @@ -115,42 +121,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg 8, 8, 8, }; - /// - /// Gets the SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes: - /// - the marker length "\x00\x0c", - /// - the number of components "\x03", - /// - component 1 uses DC table 0 and AC table 0 "\x01\x00", - /// - component 2 uses DC table 1 and AC table 1 "\x02\x11", - /// - component 3 uses DC table 1 and AC table 1 "\x03\x11", - /// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for - /// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) - /// should be 0x00, 0x3f, 0x00<<4 | 0x00. - /// - // The C# compiler emits this as a compile-time constant embedded in the PE file. - // This is effectively compiled down to: return new ReadOnlySpan(&data, length) - // More details can be found: https://github.com/dotnet/roslyn/pull/24621 - private static ReadOnlySpan SosHeaderYCbCr => new byte[] - { - JpegConstants.Markers.XFF, JpegConstants.Markers.SOS, - - // Marker - 0x00, 0x0c, - - // Length (high byte, low byte), must be 6 + 2 * (number of components in scan) - 0x03, // Number of components in a scan, 3 - 0x01, // Component Id Y - 0x00, // DC/AC Huffman table - 0x02, // Component Id Cb - 0x11, // DC/AC Huffman table - 0x03, // Component Id Cr - 0x11, // DC/AC Huffman table - 0x00, // Ss - Start of spectral selection. - 0x3f, // Se - End of spectral selection. - 0x00 - - // Ah + Ah (Successive approximation bit position high + low) - }; - /// /// Gets the unscaled quantization tables in zig-zag order. Each /// encoder copies and scales the tables according to its quality parameter. @@ -212,10 +182,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.outputStream = stream; ImageMetadata metadata = image.Metadata; + // Compute number of components based on color type in options. + int componentCount = (this.colorType == JpegColorType.Luminance) ? 1 : 3; + // System.Drawing produces identical output for jpegs with a quality parameter of 0 and 1. int qlty = Numerics.Clamp(this.quality ?? metadata.GetJpegMetadata().Quality, 1, 100); this.subsample ??= qlty >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420; + // Force SubSample into Grayscale for single component ColorType. + this.subsample = (componentCount == 1) ? JpegSubsample.Grayscale : this.subsample; + // Convert from a quality rating to a scaling factor. int scale; if (qlty < 50) @@ -229,10 +205,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg // Initialize the quantization tables. InitQuantizationTable(0, scale, ref this.luminanceQuantTable); - InitQuantizationTable(1, scale, ref this.chrominanceQuantTable); - - // Compute number of components based on input image type. - const int componentCount = 3; + if (componentCount > 1) + { + InitQuantizationTable(1, scale, ref this.chrominanceQuantTable); + } // Write the Start Of Image marker. this.WriteApplicationHeader(metadata); @@ -250,7 +226,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.WriteDefineHuffmanTables(componentCount); // Write the image data. - this.WriteStartOfScan(image, cancellationToken); + this.WriteStartOfScan(image, componentCount, cancellationToken); // Write the End Of Image marker. this.buffer[0] = JpegConstants.Markers.XFF; @@ -468,6 +444,55 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } } + /// + /// Encodes the image with no chroma, just luminance. + /// + /// The pixel format. + /// The pixel accessor providing access to the image pixels. + /// The token to monitor for cancellation. + /// The reference to the emit buffer. + private void EncodeGrayscale(Image pixels, CancellationToken cancellationToken, ref byte emitBufferBase) + where TPixel : unmanaged, IPixel + { + // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) + // (Partially done with YCbCrForwardConverter) + Block8x8F temp1 = default; + Block8x8F temp2 = default; + + Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; + + var unzig = ZigZag.CreateUnzigTable(); + + // ReSharper disable once InconsistentNaming + int prevDCY = 0; + + var pixelConverter = LuminanceForwardConverter.Create(); + ImageFrame frame = pixels.Frames.RootFrame; + Buffer2D pixelBuffer = frame.PixelBuffer; + RowOctet currentRows = default; + + for (int y = 0; y < pixels.Height; y += 8) + { + cancellationToken.ThrowIfCancellationRequested(); + currentRows.Update(pixelBuffer, y); + + for (int x = 0; x < pixels.Width; x += 8) + { + pixelConverter.Convert(frame, x, y, ref currentRows); + + prevDCY = this.WriteBlock( + QuantIndex.Luminance, + prevDCY, + ref pixelConverter.Y, + ref temp1, + ref temp2, + ref onStackLuminanceQuantTable, + ref unzig, + ref emitBufferBase); + } + } + } + /// /// Writes the application header containing the JFIF identifier plus extra data. /// @@ -898,6 +923,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg switch (this.subsample) { + case JpegSubsample.Grayscale: + subsamples = stackalloc byte[] + { + 0x11, + 0x00, + 0x00 + }; + break; case JpegSubsample.Ratio444: subsamples = stackalloc byte[] { @@ -926,26 +959,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.buffer[4] = (byte)(width & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported this.buffer[5] = (byte)componentCount; - // Number of components (1 byte), usually 1 = Gray scaled, 3 = color YCbCr or YIQ, 4 = color CMYK) - if (componentCount == 1) + for (int i = 0; i < componentCount; i++) { - this.buffer[6] = 1; + int i3 = 3 * i; + this.buffer[i3 + 6] = (byte)(i + 1); - // No subsampling for grayscale images. - this.buffer[7] = 0x11; - this.buffer[8] = 0x00; - } - else - { - for (int i = 0; i < componentCount; i++) - { - int i3 = 3 * i; - this.buffer[i3 + 6] = (byte)(i + 1); - - // We use 4:2:0 chroma subsampling by default. - this.buffer[i3 + 7] = subsamples[i]; - this.buffer[i3 + 8] = chroma[i]; - } + this.buffer[i3 + 7] = subsamples[i]; + this.buffer[i3 + 8] = chroma[i]; } this.outputStream.Write(this.buffer, 0, (3 * (componentCount - 1)) + 9); @@ -956,16 +976,60 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// The pixel format. /// The pixel accessor providing access to the image pixels. + /// The number of components in a pixel. /// The token to monitor for cancellation. - private void WriteStartOfScan(Image image, CancellationToken cancellationToken) + private void WriteStartOfScan(Image image, int componentCount, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) - // TODO: We should allow grayscale writing. - this.outputStream.Write(SosHeaderYCbCr); + Span componentId = stackalloc byte[] + { + 0x01, + 0x02, + 0x03 + }; + Span huffmanId = stackalloc byte[] + { + 0x00, + 0x11, + 0x11 + }; + + // Write the SOS (Start Of Scan) marker "\xff\xda" followed by 12 bytes: + // - the marker length "\x00\x0c", + // - the number of components "\x03", + // - component 1 uses DC table 0 and AC table 0 "\x01\x00", + // - component 2 uses DC table 1 and AC table 1 "\x02\x11", + // - component 3 uses DC table 1 and AC table 1 "\x03\x11", + // - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for + // sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) + // should be 0x00, 0x3f, 0x00<<4 | 0x00. + this.buffer[0] = JpegConstants.Markers.XFF; + this.buffer[1] = JpegConstants.Markers.SOS; + + // Length (high byte, low byte), must be 6 + 2 * (number of components in scan) + int sosSize = 6 + (2 * componentCount); + this.buffer[2] = 0x00; + this.buffer[3] = (byte)sosSize; + this.buffer[4] = (byte)componentCount; // Number of components in a scan + for (int i = 0; i < componentCount; i++) + { + int i2 = 2 * i; + this.buffer[i2 + 5] = componentId[i]; // Component Id + this.buffer[i2 + 6] = huffmanId[i]; // DC/AC Huffman table + } + + this.buffer[sosSize - 1] = 0x00; // Ss - Start of spectral selection. + this.buffer[sosSize] = 0x3f; // Se - End of spectral selection. + this.buffer[sosSize + 1] = 0x00; // Ah + Ah (Successive approximation bit position high + low) + this.outputStream.Write(this.buffer, 0, sosSize + 2); + ref byte emitBufferBase = ref MemoryMarshal.GetReference(this.emitBuffer); switch (this.subsample) { + case JpegSubsample.Grayscale: + this.EncodeGrayscale(image, cancellationToken, ref emitBufferBase); + break; case JpegSubsample.Ratio444: this.Encode444(image, cancellationToken, ref emitBufferBase); break; diff --git a/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs b/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs index 6597e0ccb8..3989bef7ad 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Jpeg @@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public enum JpegSubsample { + /// + /// Only luminance - No chrome channels. + /// + Grayscale, + /// /// High Quality - Each of the three Y'CbCr components have the same sample rate, /// thus there is no chroma subsampling. diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 6481c711ff..522c8a40b4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -83,6 +83,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public void EncodeBaseline_WorksWithDifferentSizes(TestImageProvider provider, JpegSubsample subsample, int quality) where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, subsample, quality); + [Theory] + [WithFile(TestImages.Png.BikeGrayscale, nameof(BitsPerPixel_Quality), PixelTypes.L8)] + [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L8)] + public void EncodeBaseline_GrayscaleWorksWithDifferentSizes(TestImageProvider provider, JpegSubsample subsample, int quality) + where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, subsample, quality, JpegColorType.Luminance); + [Theory] [WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 48, PixelTypes.Rgba32 | PixelTypes.Bgra32)] public void EncodeBaseline_IsNotBoundToSinglePixelType(TestImageProvider provider, JpegSubsample subsample, int quality) @@ -101,7 +107,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg : ImageComparer.TolerantPercentage(5f); provider.LimitAllocatorBufferCapacity().InBytesSqrt(200); - TestJpegEncoderCore(provider, subsample, 100, comparer); + TestJpegEncoderCore(provider, subsample, 100, JpegColorType.YCbCr, comparer); } /// @@ -131,6 +137,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImageProvider provider, JpegSubsample subsample, int quality = 100, + JpegColorType colorType = JpegColorType.YCbCr, ImageComparer comparer = null) where TPixel : unmanaged, IPixel { @@ -142,7 +149,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var encoder = new JpegEncoder { Subsample = subsample, - Quality = quality + Quality = quality, + ColorType = colorType }; string info = $"{subsample}-Q{quality}"; From 6cff023550b677f459394a9845e18315b250b1e0 Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Mon, 29 Mar 2021 21:59:50 +0200 Subject: [PATCH 13/25] Add test cases for Grayscale jpeg --- tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 522c8a40b4..0d277a132c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -80,13 +80,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 255, 100, 50, 255, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(BitsPerPixel_Quality), 7, 5, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(BitsPerPixel_Quality), 600, 400, PixelTypes.Rgba32)] + [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L8)] public void EncodeBaseline_WorksWithDifferentSizes(TestImageProvider provider, JpegSubsample subsample, int quality) where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, subsample, quality); [Theory] [WithFile(TestImages.Png.BikeGrayscale, nameof(BitsPerPixel_Quality), PixelTypes.L8)] + [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.Rgba32)] [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L8)] - public void EncodeBaseline_GrayscaleWorksWithDifferentSizes(TestImageProvider provider, JpegSubsample subsample, int quality) + [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L16)] + [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.La16)] + [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.La32)] + public void EncodeBaseline_Grayscale(TestImageProvider provider, JpegSubsample subsample, int quality) where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, subsample, quality, JpegColorType.Luminance); [Theory] From 6dbde61f8df15651caa51f7d908f855de660d792 Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Mon, 29 Mar 2021 22:01:24 +0200 Subject: [PATCH 14/25] Rename JpegEncoder InitializeColorType method --- src/ImageSharp/Formats/Jpeg/JpegEncoder.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index 6842cfe15a..c4439b8045 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg where TPixel : unmanaged, IPixel { var encoder = new JpegEncoderCore(this); - this.EnrichColorType(); + this.InitializeColorType(); encoder.Encode(image, stream); } @@ -56,19 +56,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg where TPixel : unmanaged, IPixel { var encoder = new JpegEncoderCore(this); - this.EnrichColorType(); + this.InitializeColorType(); return encoder.EncodeAsync(image, stream, cancellationToken); } /// /// If ColorType was not set, set it based on the given . /// - private void EnrichColorType() + private void InitializeColorType() where TPixel : unmanaged, IPixel { if (this.ColorType == null) { - bool isGrayscale = typeof(TPixel) == typeof(L8) || typeof(TPixel) == typeof(L16); + bool isGrayscale = + typeof(TPixel) == typeof(L8) || typeof(TPixel) == typeof(L16) || + typeof(TPixel) == typeof(La16) || typeof(TPixel) == typeof(La32); this.ColorType = isGrayscale ? JpegColorType.Luminance : JpegColorType.YCbCr; } } From 26d61124b590a64bb85fcebd569cf8bc26e0cd26 Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Mon, 29 Mar 2021 22:13:53 +0200 Subject: [PATCH 15/25] LuminanceForwardConverter handles the entire conversion itself --- .../Jpeg/Components/Encoder/L8ToYConverter.cs | 49 ------------------- .../LuminanceForwardConverter{TPixel}.cs | 14 +++--- 2 files changed, 7 insertions(+), 56 deletions(-) delete mode 100644 src/ImageSharp/Formats/Jpeg/Components/Encoder/L8ToYConverter.cs diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/L8ToYConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/L8ToYConverter.cs deleted file mode 100644 index 6d787c58ff..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/L8ToYConverter.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder -{ - /// - /// Provides 8-bit lookup tables for converting from L8 to Y colorspace. - /// - internal unsafe struct L8ToYConverter - { - /// - /// Initializes - /// - /// The initialized - public static L8ToYConverter Create() - { - L8ToYConverter converter = default; - return converter; - } - - /// - /// Optimized method to allocates the correct y, cb, and cr values to the DCT blocks from the given r, g, b values. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ConvertPixelInto( - int l, - ref Block8x8F yResult, - int i) => yResult[i] = l; - - public void Convert(Span l8Span, ref Block8x8F yBlock) - { - ref L8 l8Start = ref l8Span[0]; - - for (int i = 0; i < 64; i++) - { - ref L8 c = ref Unsafe.Add(ref l8Start, i); - - this.ConvertPixelInto( - c.PackedValue, - ref yBlock, - i); - } - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs index 0b6cde8269..cc81130dd7 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; @@ -19,11 +20,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// public Block8x8F Y; - /// - /// The converter - /// - private L8ToYConverter converter; - /// /// Temporal 8x8 block to hold TPixel data /// @@ -37,7 +33,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder public static LuminanceForwardConverter Create() { var result = default(LuminanceForwardConverter); - result.converter = L8ToYConverter.Create(); return result; } @@ -52,8 +47,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder PixelOperations.Instance.ToL8(frame.GetConfiguration(), this.pixelBlock.AsSpanUnsafe(), l8Span); ref Block8x8F yBlock = ref this.Y; + ref L8 l8Start = ref l8Span[0]; - this.converter.Convert(l8Span, ref yBlock); + for (int i = 0; i < 64; i++) + { + ref L8 c = ref Unsafe.Add(ref l8Start, i); + yBlock[i] = c.PackedValue; + } } } } From 096ef3d3aa1189c1abcebf03ee7deff30a138874 Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Tue, 30 Mar 2021 18:13:05 +0200 Subject: [PATCH 16/25] Refactor out JpegSubsample.Grayscale --- .../Formats/Jpeg/JpegEncoderCore.cs | 81 ++++++++++--------- src/ImageSharp/Formats/Jpeg/JpegSubsample.cs | 5 -- .../Formats/Jpg/JpegEncoderTests.cs | 30 ++++--- 3 files changed, 62 insertions(+), 54 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index bca03b109b..f5dc1c79fe 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -189,9 +189,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg int qlty = Numerics.Clamp(this.quality ?? metadata.GetJpegMetadata().Quality, 1, 100); this.subsample ??= qlty >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420; - // Force SubSample into Grayscale for single component ColorType. - this.subsample = (componentCount == 1) ? JpegSubsample.Grayscale : this.subsample; - // Convert from a quality rating to a scaling factor. int scale; if (qlty < 50) @@ -921,32 +918,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg 0x01 }; - switch (this.subsample) + if (this.colorType == JpegColorType.Luminance) { - case JpegSubsample.Grayscale: - subsamples = stackalloc byte[] - { - 0x11, - 0x00, - 0x00 - }; - break; - case JpegSubsample.Ratio444: - subsamples = stackalloc byte[] - { - 0x11, - 0x11, - 0x11 - }; - break; - case JpegSubsample.Ratio420: - subsamples = stackalloc byte[] - { - 0x22, - 0x11, - 0x11 - }; - break; + subsamples = stackalloc byte[] + { + 0x11, + 0x00, + 0x00 + }; + } + else + { + switch (this.subsample) + { + case JpegSubsample.Ratio444: + subsamples = stackalloc byte[] + { + 0x11, + 0x11, + 0x11 + }; + break; + case JpegSubsample.Ratio420: + subsamples = stackalloc byte[] + { + 0x22, + 0x11, + 0x11 + }; + break; + } } // Length (high byte, low byte), 8 + components * 3. @@ -1025,17 +1026,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.outputStream.Write(this.buffer, 0, sosSize + 2); ref byte emitBufferBase = ref MemoryMarshal.GetReference(this.emitBuffer); - switch (this.subsample) + if (this.colorType == JpegColorType.Luminance) { - case JpegSubsample.Grayscale: - this.EncodeGrayscale(image, cancellationToken, ref emitBufferBase); - break; - case JpegSubsample.Ratio444: - this.Encode444(image, cancellationToken, ref emitBufferBase); - break; - case JpegSubsample.Ratio420: - this.Encode420(image, cancellationToken, ref emitBufferBase); - break; + this.EncodeGrayscale(image, cancellationToken, ref emitBufferBase); + } + else + { + switch (this.subsample) + { + case JpegSubsample.Ratio444: + this.Encode444(image, cancellationToken, ref emitBufferBase); + break; + case JpegSubsample.Ratio420: + this.Encode420(image, cancellationToken, ref emitBufferBase); + break; + } } // Pad the last byte with 1's. diff --git a/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs b/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs index 3989bef7ad..16488f6d21 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs @@ -8,11 +8,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public enum JpegSubsample { - /// - /// Only luminance - No chrome channels. - /// - Grayscale, - /// /// High Quality - Each of the three Y'CbCr components have the same sample rate, /// thus there is no chroma subsampling. diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 0d277a132c..9a1d423a6d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -40,6 +40,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { JpegSubsample.Ratio444, 100 }, }; + public static readonly TheoryData Grayscale_Quality = + new TheoryData + { + { 40 }, + { 60 }, + { 100 } + }; + public static readonly TheoryData RatioFiles = new TheoryData { @@ -85,14 +93,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, subsample, quality); [Theory] - [WithFile(TestImages.Png.BikeGrayscale, nameof(BitsPerPixel_Quality), PixelTypes.L8)] - [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.Rgba32)] - [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L8)] - [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L16)] - [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.La16)] - [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.La32)] - public void EncodeBaseline_Grayscale(TestImageProvider provider, JpegSubsample subsample, int quality) - where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, subsample, quality, JpegColorType.Luminance); + [WithFile(TestImages.Png.BikeGrayscale, nameof(Grayscale_Quality), PixelTypes.L8)] + [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.Rgba32, 100)] + [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.L8, 100)] + [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.L16, 100)] + [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La16, 100)] + [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La32, 100)] + public void EncodeBaseline_Grayscale(TestImageProvider provider, int quality) + where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, null, quality, JpegColorType.Luminance); [Theory] [WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 48, PixelTypes.Rgba32 | PixelTypes.Bgra32)] @@ -118,7 +126,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg /// /// Anton's SUPER-SCIENTIFIC tolerance threshold calculation /// - private static ImageComparer GetComparer(int quality, JpegSubsample subsample) + private static ImageComparer GetComparer(int quality, JpegSubsample? subsample) { float tolerance = 0.015f; // ~1.5% @@ -140,7 +148,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg private static void TestJpegEncoderCore( TestImageProvider provider, - JpegSubsample subsample, + JpegSubsample? subsample, int quality = 100, JpegColorType colorType = JpegColorType.YCbCr, ImageComparer comparer = null) @@ -311,7 +319,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public async Task Encode_IsCancellable(JpegSubsample subsample, int cancellationDelayMs) { using var image = new Image(5000, 5000); - using MemoryStream stream = new MemoryStream(); + using var stream = new MemoryStream(); var cts = new CancellationTokenSource(); if (cancellationDelayMs == 0) { From d844e707601c5a4bfe8bf0e28e2ba89262b940e4 Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Tue, 30 Mar 2021 21:37:45 +0200 Subject: [PATCH 17/25] Add JpegColorType to JpegMetadata and use it to configure the JpegEncoder --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 2 ++ src/ImageSharp/Formats/Jpeg/JpegEncoder.cs | 16 ++++++++++++---- src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 15 ++++++++++++--- .../Formats/Jpg/JpegMetadataTests.cs | 4 +++- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index c4355cdbe1..f7cc82c7da 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -1038,6 +1038,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.Frame.ComponentOrder[i] = (byte)componentIndex; } + this.Metadata.GetJpegMetadata().ColorType = (selectorsCount == 1) ? JpegColorType.Luminance : JpegColorType.YCbCr; + stream.Read(this.temp, 0, 3); int spectralStart = this.temp[0]; diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index c4439b8045..8131f74d26 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg where TPixel : unmanaged, IPixel { var encoder = new JpegEncoderCore(this); - this.InitializeColorType(); + this.InitializeColorType(image); encoder.Encode(image, stream); } @@ -56,16 +56,24 @@ namespace SixLabors.ImageSharp.Formats.Jpeg where TPixel : unmanaged, IPixel { var encoder = new JpegEncoderCore(this); - this.InitializeColorType(); + this.InitializeColorType(image); return encoder.EncodeAsync(image, stream, cancellationToken); } /// - /// If ColorType was not set, set it based on the given . + /// If ColorType was not set, set it based on the given image. /// - private void InitializeColorType() + private void InitializeColorType(Image image) where TPixel : unmanaged, IPixel { + // First inspect the image metadata. + if (this.ColorType == null) + { + JpegMetadata metadata = image.Metadata.GetJpegMetadata(); + this.ColorType = metadata.ColorType; + } + + // Secondly, inspect the pixel type. if (this.ColorType == null) { bool isGrayscale = diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index c9dded6352..9670d167e0 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Jpeg @@ -19,14 +19,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// Initializes a new instance of the class. /// /// The metadata to create an instance from. - private JpegMetadata(JpegMetadata other) => this.Quality = other.Quality; + private JpegMetadata(JpegMetadata other) + { + this.Quality = other.Quality; + this.ColorType = other.ColorType; + } /// /// Gets or sets the encoded quality. /// public int Quality { get; set; } = 75; + /// + /// Gets or sets the encoded quality. + /// + public JpegColorType? ColorType { get; set; } + /// public IDeepCloneable DeepClone() => new JpegMetadata(this); } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs index e14ec81c67..503ede1299 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs @@ -12,12 +12,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Fact] public void CloneIsDeep() { - var meta = new JpegMetadata { Quality = 50 }; + var meta = new JpegMetadata { Quality = 50, ColorType = JpegColorType.Luminance }; var clone = (JpegMetadata)meta.DeepClone(); clone.Quality = 99; + clone.ColorType = JpegColorType.YCbCr; Assert.False(meta.Quality.Equals(clone.Quality)); + Assert.False(meta.ColorType.Equals(clone.ColorType)); } } } From 4d0fb4070a71d96378c77a1389ae430421da7fd2 Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Tue, 30 Mar 2021 22:00:04 +0200 Subject: [PATCH 18/25] Put setting JpegColorType and ColorSpace close together --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index f7cc82c7da..8571cf0ec3 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -912,6 +912,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.Frame.MaxHorizontalFactor = maxH; this.Frame.MaxVerticalFactor = maxV; this.ColorSpace = this.DeduceJpegColorSpace(); + this.Metadata.GetJpegMetadata().ColorType = this.ColorSpace == JpegColorSpace.Grayscale ? JpegColorType.Luminance : JpegColorType.YCbCr; this.Frame.InitComponents(); this.ImageSizeInMCU = new Size(this.Frame.McusPerLine, this.Frame.McusPerColumn); } @@ -1038,8 +1039,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.Frame.ComponentOrder[i] = (byte)componentIndex; } - this.Metadata.GetJpegMetadata().ColorType = (selectorsCount == 1) ? JpegColorType.Luminance : JpegColorType.YCbCr; - stream.Read(this.temp, 0, 3); int spectralStart = this.temp[0]; From b1364dd065862b80e266bb613ae1e993b248ccc7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 8 Apr 2021 23:33:11 +0100 Subject: [PATCH 19/25] Update shared-infrastructure --- shared-infrastructure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-infrastructure b/shared-infrastructure index 06a7339834..9b1179f0eb 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 06a733983486638b9e38197c7c6eb197ecac43e6 +Subproject commit 9b1179f0ebe6a4dfed998252b860fa07fee54363 From dc0982f1864b0c41fcc1a79853b3d16746906835 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 14 Apr 2021 16:09:25 +0100 Subject: [PATCH 20/25] Faster Linear Transforms (#1591) * Attempt to use same weight generation algorithm as resize. * tests pass * Identical output * Update LinearTransformKernelFactory{TResampler}.cs * Use new low allocation iterator * Migrate projective transforms. * Optimizations * Smaller kernel * Fix sampling accuracy * Finalize and update refs * Revert unnecessary changes * Remove enumerator * Actually save output for debugging. * Use custom test png encoder for reduced memory environments * Convolution should use scaled vectors * Update TestEnvironmentTests.cs * Try using doubles * Moar double precision * Fix radius calculation * Test if issue is SIMD related. * Detect runtime to determine pipeline. * Fix stack overflow * fix condition * Try simplified scalar run * Simplify unpremultiply scalar * Update Numerics.cs * Fix runtime environment * Update ImageSharp.csproj * Duplicate the caller with scalar versions * Update method name, exclude from coverage. * Don't save output during coverage tests for perf. * Update src/ImageSharp/Common/Helpers/RuntimeEnvironment.cs Co-authored-by: Anton Firszov Co-authored-by: Anton Firszov --- .gitattributes | 3 +- .../Common/Helpers/RuntimeEnvironment.cs | 32 +++ .../AffineTransformProcessor{TPixel}.cs | 210 ++++++++++++------ .../Linear/LinearTransformUtility.cs | 60 +++++ .../Transforms/Linear/LinearTransformUtils.cs | 104 --------- .../ProjectiveTransformProcessor{TPixel}.cs | 209 +++++++++++------ .../Transforms/Resize/ResizeKernel.cs | 4 +- .../Processing/Rotate.cs | 28 ++- .../ImageSharp.Benchmarks/Processing/Skew.cs | 28 ++- .../Helpers/RuntimeEnvironmentTests.cs | 41 ++++ .../Transforms/AffineTransformTests.cs | 5 +- .../TestUtilities/ImagingTestCaseUtility.cs | 2 +- ...SharpPngEncoderWithDefaultConfiguration.cs | 94 ++++++++ .../TestUtilities/TestEnvironment.Formats.cs | 4 +- .../TestUtilities/TestEnvironment.cs | 7 + .../TestUtilities/TestImageExtensions.cs | 61 ++--- .../Tests/TestEnvironmentTests.cs | 3 +- ...tPattern100x50_R(50)_S(1,1)_T(-20,-10).png | 4 +- ...ate_Rgba32_TestPattern100x50__original.png | 4 +- ...pler_Rgba32_TestPattern150x150_Bicubic.png | 4 +- ...r_Rgba32_TestPattern150x150_CatmullRom.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos2.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos3.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos5.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos8.png | 4 +- ...2_TestPattern150x150_MitchellNetravali.png | 4 +- ...ler_Rgba32_TestPattern150x150_Robidoux.png | 4 +- ...gba32_TestPattern150x150_RobidouxSharp.png | 4 +- ...mpler_Rgba32_TestPattern150x150_Spline.png | 4 +- ...ler_Rgba32_TestPattern150x150_Triangle.png | 4 +- ...ampler_Rgba32_TestPattern150x150_Welch.png | 4 +- .../DrawImageTests/DrawTransformed.png | 4 +- 32 files changed, 604 insertions(+), 351 deletions(-) create mode 100644 src/ImageSharp/Common/Helpers/RuntimeEnvironment.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs delete mode 100644 src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtils.cs create mode 100644 tests/ImageSharp.Tests/Helpers/RuntimeEnvironmentTests.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ImageSharpPngEncoderWithDefaultConfiguration.cs diff --git a/.gitattributes b/.gitattributes index 416dd0d06f..70ced69033 100644 --- a/.gitattributes +++ b/.gitattributes @@ -86,7 +86,6 @@ *.dll binary *.eot binary *.exe binary -*.ktx binary *.otf binary *.pbm binary *.pdf binary @@ -125,3 +124,5 @@ *.tga filter=lfs diff=lfs merge=lfs -text *.webp filter=lfs diff=lfs merge=lfs -text *.dds filter=lfs diff=lfs merge=lfs -text +*.ktx filter=lfs diff=lfs merge=lfs -text +*.ktx2 filter=lfs diff=lfs merge=lfs -text diff --git a/src/ImageSharp/Common/Helpers/RuntimeEnvironment.cs b/src/ImageSharp/Common/Helpers/RuntimeEnvironment.cs new file mode 100644 index 0000000000..5525d3de50 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/RuntimeEnvironment.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Provides information about the .NET runtime installation. + /// Many methods defer to when available. + /// + internal static class RuntimeEnvironment + { + private static readonly Lazy IsNetCoreLazy = new Lazy(() => FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase)); + + /// + /// Gets a value indicating whether the .NET installation is .NET Core 3.1 or lower. + /// + public static bool IsNetCore => IsNetCoreLazy.Value; + + /// + /// Gets the name of the .NET installation on which an app is running. + /// + public static string FrameworkDescription => RuntimeInformation.FrameworkDescription; + + /// + /// Indicates whether the current application is running on the specified platform. + /// + public static bool IsOSPlatform(OSPlatform osPlatform) => RuntimeInformation.IsOSPlatform(osPlatform); + } +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs index c08f5d3d3b..5f04918e09 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; +using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -80,32 +82,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - int yRadius = LinearTransformUtils.GetSamplingRadius(in sampler, source.Height, destination.Height); - int xRadius = LinearTransformUtils.GetSamplingRadius(in sampler, source.Width, destination.Width); - var radialExtents = new Vector2(xRadius, yRadius); - int yLength = (yRadius * 2) + 1; - int xLength = (xRadius * 2) + 1; - - // We use 2D buffers so that we can access the weight spans per row in parallel. - using Buffer2D yKernelBuffer = configuration.MemoryAllocator.Allocate2D(yLength, destination.Height); - using Buffer2D xKernelBuffer = configuration.MemoryAllocator.Allocate2D(xLength, destination.Height); - - int maxX = source.Width - 1; - int maxY = source.Height - 1; - var maxSourceExtents = new Vector4(maxX, maxY, maxX, maxY); - var operation = new AffineOperation( configuration, source, destination, - yKernelBuffer, - xKernelBuffer, in sampler, - matrix, - radialExtents, - maxSourceExtents); + matrix); - ParallelRowIterator.IterateRows, Vector4>( + ParallelRowIterator.IterateRowIntervals, Vector4>( configuration, destination.Bounds(), in operation); @@ -117,7 +101,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; private readonly Rectangle bounds; private readonly Matrix3x2 matrix; - private readonly int maxX; [MethodImpl(InliningOptions.ShortMethod)] public NNAffineOperation( @@ -129,15 +112,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.destination = destination; this.bounds = source.Bounds(); this.matrix = matrix; - this.maxX = destination.Width; } [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y) { + Buffer2D sourceBuffer = this.source.PixelBuffer; Span destRow = this.destination.GetPixelRowSpan(y); - for (int x = 0; x < this.maxX; x++) + for (int x = 0; x < destRow.Length; x++) { var point = Vector2.Transform(new Vector2(x, y), this.matrix); int px = (int)MathF.Round(point.X); @@ -145,84 +128,181 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms if (this.bounds.Contains(px, py)) { - destRow[x] = this.source[px, py]; + destRow[x] = sourceBuffer.GetElementUnsafe(px, py); } } } } - private readonly struct AffineOperation : IRowOperation + private readonly struct AffineOperation : IRowIntervalOperation where TResampler : struct, IResampler { private readonly Configuration configuration; private readonly ImageFrame source; private readonly ImageFrame destination; - private readonly Buffer2D yKernelBuffer; - private readonly Buffer2D xKernelBuffer; private readonly TResampler sampler; private readonly Matrix3x2 matrix; - private readonly Vector2 radialExtents; - private readonly Vector4 maxSourceExtents; - private readonly int maxX; + private readonly float yRadius; + private readonly float xRadius; [MethodImpl(InliningOptions.ShortMethod)] public AffineOperation( Configuration configuration, ImageFrame source, ImageFrame destination, - Buffer2D yKernelBuffer, - Buffer2D xKernelBuffer, in TResampler sampler, - Matrix3x2 matrix, - Vector2 radialExtents, - Vector4 maxSourceExtents) + Matrix3x2 matrix) { this.configuration = configuration; this.source = source; this.destination = destination; - this.yKernelBuffer = yKernelBuffer; - this.xKernelBuffer = xKernelBuffer; this.sampler = sampler; this.matrix = matrix; - this.radialExtents = radialExtents; - this.maxSourceExtents = maxSourceExtents; - this.maxX = destination.Width; + + this.yRadius = LinearTransformUtility.GetSamplingRadius(in sampler, source.Height, destination.Height); + this.xRadius = LinearTransformUtility.GetSamplingRadius(in sampler, source.Width, destination.Width); } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y, Span span) + public void Invoke(in RowInterval rows, Span span) { - Buffer2D sourceBuffer = this.source.PixelBuffer; + if (RuntimeEnvironment.IsOSPlatform(OSPlatform.OSX) + && RuntimeEnvironment.IsNetCore) + { + // There's something wrong with the JIT in .NET Core 3.1 on certain + // MacOSX machines so we have to use different pipelines. + // It's: + // - Not reproducable locally + // - Doesn't seem to be triggered by the bulk Numerics.UnPremultiply method but by caller. + // https://github.com/SixLabors/ImageSharp/pull/1591 + this.InvokeMacOSX(in rows, span); + return; + } - PixelOperations.Instance.ToVector4( - this.configuration, - this.destination.GetPixelRowSpan(y), - span); + Matrix3x2 matrix = this.matrix; + TResampler sampler = this.sampler; + float yRadius = this.yRadius; + float xRadius = this.xRadius; + int maxY = this.source.Height - 1; + int maxX = this.source.Width - 1; - ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y)); - ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y)); + Buffer2D sourceBuffer = this.source.PixelBuffer; - for (int x = 0; x < this.maxX; x++) + for (int y = rows.Min; y < rows.Max; y++) { - // Use the single precision position to calculate correct bounding pixels - // otherwise we get rogue pixels outside of the bounds. - var point = Vector2.Transform(new Vector2(x, y), this.matrix); - LinearTransformUtils.Convolve( - in this.sampler, - point, - sourceBuffer, + Span rowSpan = this.destination.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4( + this.configuration, + rowSpan, + span, + PixelConversionModifiers.Scale); + + for (int x = 0; x < span.Length; x++) + { + var point = Vector2.Transform(new Vector2(x, y), matrix); + float pY = point.Y; + float pX = point.X; + + int top = LinearTransformUtility.GetRangeStart(yRadius, pY, maxY); + int bottom = LinearTransformUtility.GetRangeEnd(yRadius, pY, maxY); + int left = LinearTransformUtility.GetRangeStart(xRadius, pX, maxX); + int right = LinearTransformUtility.GetRangeEnd(xRadius, pX, maxX); + + if (bottom == top || right == left) + { + continue; + } + + Vector4 sum = Vector4.Zero; + for (int yK = top; yK <= bottom; yK++) + { + float yWeight = sampler.GetValue(yK - pY); + + for (int xK = left; xK <= right; xK++) + { + float xWeight = sampler.GetValue(xK - pX); + + Vector4 current = sourceBuffer.GetElementUnsafe(xK, yK).ToScaledVector4(); + Numerics.Premultiply(ref current); + sum += current * xWeight * yWeight; + } + } + + span[x] = sum; + } + + Numerics.UnPremultiply(span); + PixelOperations.Instance.FromVector4Destructive( + this.configuration, span, - x, - ref yKernelSpanRef, - ref xKernelSpanRef, - this.radialExtents, - this.maxSourceExtents); + rowSpan, + PixelConversionModifiers.Scale); } + } + + [ExcludeFromCodeCoverage] + [MethodImpl(InliningOptions.ShortMethod)] + private void InvokeMacOSX(in RowInterval rows, Span span) + { + Matrix3x2 matrix = this.matrix; + TResampler sampler = this.sampler; + float yRadius = this.yRadius; + float xRadius = this.xRadius; + int maxY = this.source.Height - 1; + int maxX = this.source.Width - 1; - PixelOperations.Instance.FromVector4Destructive( - this.configuration, - span, - this.destination.GetPixelRowSpan(y)); + Buffer2D sourceBuffer = this.source.PixelBuffer; + + for (int y = rows.Min; y < rows.Max; y++) + { + Span rowSpan = this.destination.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4( + this.configuration, + rowSpan, + span, + PixelConversionModifiers.Scale); + + for (int x = 0; x < span.Length; x++) + { + var point = Vector2.Transform(new Vector2(x, y), matrix); + float pY = point.Y; + float pX = point.X; + + int top = LinearTransformUtility.GetRangeStart(yRadius, pY, maxY); + int bottom = LinearTransformUtility.GetRangeEnd(yRadius, pY, maxY); + int left = LinearTransformUtility.GetRangeStart(xRadius, pX, maxX); + int right = LinearTransformUtility.GetRangeEnd(xRadius, pX, maxX); + + if (bottom == top || right == left) + { + continue; + } + + Vector4 sum = Vector4.Zero; + for (int yK = top; yK <= bottom; yK++) + { + float yWeight = sampler.GetValue(yK - pY); + + for (int xK = left; xK <= right; xK++) + { + float xWeight = sampler.GetValue(xK - pX); + + Vector4 current = sourceBuffer.GetElementUnsafe(xK, yK).ToScaledVector4(); + Numerics.Premultiply(ref current); + sum += current * xWeight * yWeight; + } + } + + Numerics.UnPremultiply(ref sum); + span[x] = sum; + } + + PixelOperations.Instance.FromVector4Destructive( + this.configuration, + span, + rowSpan, + PixelConversionModifiers.Scale); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs new file mode 100644 index 0000000000..c6168b4619 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs @@ -0,0 +1,60 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Utility methods for linear transforms. + /// + internal static class LinearTransformUtility + { + /// + /// Returns the sampling radius for the given sampler and dimensions. + /// + /// The type of resampler. + /// The resampler sampler. + /// The source size. + /// The destination size. + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static float GetSamplingRadius(in TResampler sampler, int sourceSize, int destinationSize) + where TResampler : struct, IResampler + { + float scale = (float)sourceSize / destinationSize; + + if (scale < 1F) + { + scale = 1F; + } + + return MathF.Ceiling(sampler.Radius * scale); + } + + /// + /// Gets the start position (inclusive) for a sampling range given + /// the radius, center position and max constraint. + /// + /// The radius. + /// The center position. + /// The max allowed amouunt. + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static int GetRangeStart(float radius, float center, int max) + => Numerics.Clamp((int)MathF.Ceiling(center - radius), 0, max); + + /// + /// Gets the end position (inclusive) for a sampling range given + /// the radius, center position and max constraint. + /// + /// The radius. + /// The center position. + /// The max allowed amouunt. + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static int GetRangeEnd(float radius, float center, int max) + => Numerics.Clamp((int)MathF.Floor(center + radius), 0, max); + } +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtils.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtils.cs deleted file mode 100644 index e65b2cbe92..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtils.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Processing.Processors.Transforms -{ - /// - /// Utility methods for affine and projective transforms. - /// - internal static class LinearTransformUtils - { - [MethodImpl(InliningOptions.ShortMethod)] - internal static int GetSamplingRadius(in TResampler sampler, int sourceSize, int destinationSize) - where TResampler : struct, IResampler - { - double scale = sourceSize / destinationSize; - if (scale < 1) - { - scale = 1; - } - - return (int)Math.Ceiling(scale * sampler.Radius); - } - - [MethodImpl(InliningOptions.ShortMethod)] - internal static void Convolve( - in TResampler sampler, - Vector2 transformedPoint, - Buffer2D sourcePixels, - Span targetRow, - int column, - ref float yKernelSpanRef, - ref float xKernelSpanRef, - Vector2 radialExtents, - Vector4 maxSourceExtents) - where TResampler : struct, IResampler - where TPixel : unmanaged, IPixel - { - // Clamp sampling pixel radial extents to the source image edges - Vector2 minXY = transformedPoint - radialExtents; - Vector2 maxXY = transformedPoint + radialExtents; - - // left, top, right, bottom - var sourceExtents = new Vector4( - MathF.Ceiling(minXY.X), - MathF.Ceiling(minXY.Y), - MathF.Floor(maxXY.X), - MathF.Floor(maxXY.Y)); - - sourceExtents = Numerics.Clamp(sourceExtents, Vector4.Zero, maxSourceExtents); - - int left = (int)sourceExtents.X; - int top = (int)sourceExtents.Y; - int right = (int)sourceExtents.Z; - int bottom = (int)sourceExtents.W; - - if (left == right || top == bottom) - { - return; - } - - CalculateWeights(in sampler, top, bottom, transformedPoint.Y, ref yKernelSpanRef); - CalculateWeights(in sampler, left, right, transformedPoint.X, ref xKernelSpanRef); - - Vector4 sum = Vector4.Zero; - for (int kernelY = 0, y = top; y <= bottom; y++, kernelY++) - { - float yWeight = Unsafe.Add(ref yKernelSpanRef, kernelY); - - for (int kernelX = 0, x = left; x <= right; x++, kernelX++) - { - float xWeight = Unsafe.Add(ref xKernelSpanRef, kernelX); - - // Values are first premultiplied to prevent darkening of edge pixels. - var current = sourcePixels[x, y].ToVector4(); - Numerics.Premultiply(ref current); - sum += current * xWeight * yWeight; - } - } - - // Reverse the premultiplication - Numerics.UnPremultiply(ref sum); - targetRow[column] = sum; - } - - [MethodImpl(InliningOptions.ShortMethod)] - private static void CalculateWeights(in TResampler sampler, int min, int max, float point, ref float weightsRef) - where TResampler : struct, IResampler - { - float sum = 0; - for (int x = 0, i = min; i <= max; i++, x++) - { - float weight = sampler.GetValue(i - point); - sum += weight; - Unsafe.Add(ref weightsRef, x) = weight; - } - } - } -} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs index f16a495b14..9396a018d3 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -80,32 +81,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - int yRadius = LinearTransformUtils.GetSamplingRadius(in sampler, source.Height, destination.Height); - int xRadius = LinearTransformUtils.GetSamplingRadius(in sampler, source.Width, destination.Width); - var radialExtents = new Vector2(xRadius, yRadius); - int yLength = (yRadius * 2) + 1; - int xLength = (xRadius * 2) + 1; - - // We use 2D buffers so that we can access the weight spans per row in parallel. - using Buffer2D yKernelBuffer = configuration.MemoryAllocator.Allocate2D(yLength, destination.Height); - using Buffer2D xKernelBuffer = configuration.MemoryAllocator.Allocate2D(xLength, destination.Height); - - int maxX = source.Width - 1; - int maxY = source.Height - 1; - var maxSourceExtents = new Vector4(maxX, maxY, maxX, maxY); - var operation = new ProjectiveOperation( configuration, source, destination, - yKernelBuffer, - xKernelBuffer, in sampler, - matrix, - radialExtents, - maxSourceExtents); + matrix); - ParallelRowIterator.IterateRows, Vector4>( + ParallelRowIterator.IterateRowIntervals, Vector4>( configuration, destination.Bounds(), in operation); @@ -117,7 +100,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ImageFrame destination; private readonly Rectangle bounds; private readonly Matrix4x4 matrix; - private readonly int maxX; [MethodImpl(InliningOptions.ShortMethod)] public NNProjectiveOperation( @@ -129,15 +111,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.destination = destination; this.bounds = source.Bounds(); this.matrix = matrix; - this.maxX = destination.Width; } [MethodImpl(InliningOptions.ShortMethod)] public void Invoke(int y) { + Buffer2D sourceBuffer = this.source.PixelBuffer; Span destRow = this.destination.GetPixelRowSpan(y); - for (int x = 0; x < this.maxX; x++) + for (int x = 0; x < destRow.Length; x++) { Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, this.matrix); int px = (int)MathF.Round(point.X); @@ -145,84 +127,181 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms if (this.bounds.Contains(px, py)) { - destRow[x] = this.source[px, py]; + destRow[x] = sourceBuffer.GetElementUnsafe(px, py); } } } } - private readonly struct ProjectiveOperation : IRowOperation + private readonly struct ProjectiveOperation : IRowIntervalOperation where TResampler : struct, IResampler { private readonly Configuration configuration; private readonly ImageFrame source; private readonly ImageFrame destination; - private readonly Buffer2D yKernelBuffer; - private readonly Buffer2D xKernelBuffer; private readonly TResampler sampler; private readonly Matrix4x4 matrix; - private readonly Vector2 radialExtents; - private readonly Vector4 maxSourceExtents; - private readonly int maxX; + private readonly float yRadius; + private readonly float xRadius; [MethodImpl(InliningOptions.ShortMethod)] public ProjectiveOperation( Configuration configuration, ImageFrame source, ImageFrame destination, - Buffer2D yKernelBuffer, - Buffer2D xKernelBuffer, in TResampler sampler, - Matrix4x4 matrix, - Vector2 radialExtents, - Vector4 maxSourceExtents) + Matrix4x4 matrix) { this.configuration = configuration; this.source = source; this.destination = destination; - this.yKernelBuffer = yKernelBuffer; - this.xKernelBuffer = xKernelBuffer; this.sampler = sampler; this.matrix = matrix; - this.radialExtents = radialExtents; - this.maxSourceExtents = maxSourceExtents; - this.maxX = destination.Width; + + this.yRadius = LinearTransformUtility.GetSamplingRadius(in sampler, source.Height, destination.Height); + this.xRadius = LinearTransformUtility.GetSamplingRadius(in sampler, source.Width, destination.Width); } [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y, Span span) + public void Invoke(in RowInterval rows, Span span) { - Buffer2D sourceBuffer = this.source.PixelBuffer; + if (RuntimeEnvironment.IsOSPlatform(OSPlatform.OSX) + && RuntimeEnvironment.IsNetCore) + { + // There's something wrong with the JIT in .NET Core 3.1 on certain + // MacOSX machines so we have to use different pipelines. + // It's: + // - Not reproducable locally + // - Doesn't seem to be triggered by the bulk Numerics.UnPremultiply method but by caller. + // https://github.com/SixLabors/ImageSharp/pull/1591 + this.InvokeMacOSX(in rows, span); + return; + } - PixelOperations.Instance.ToVector4( - this.configuration, - this.destination.GetPixelRowSpan(y), - span); + Matrix4x4 matrix = this.matrix; + TResampler sampler = this.sampler; + float yRadius = this.yRadius; + float xRadius = this.xRadius; + int maxY = this.source.Height - 1; + int maxX = this.source.Width - 1; - ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y)); - ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y)); + Buffer2D sourceBuffer = this.source.PixelBuffer; - for (int x = 0; x < this.maxX; x++) + for (int y = rows.Min; y < rows.Max; y++) { - // Use the single precision position to calculate correct bounding pixels - // otherwise we get rogue pixels outside of the bounds. - Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, this.matrix); - LinearTransformUtils.Convolve( - in this.sampler, - point, - sourceBuffer, + Span rowSpan = this.destination.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4( + this.configuration, + rowSpan, + span, + PixelConversionModifiers.Scale); + + for (int x = 0; x < span.Length; x++) + { + Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); + float pY = point.Y; + float pX = point.X; + + int top = LinearTransformUtility.GetRangeStart(yRadius, pY, maxY); + int bottom = LinearTransformUtility.GetRangeEnd(yRadius, pY, maxY); + int left = LinearTransformUtility.GetRangeStart(xRadius, pX, maxX); + int right = LinearTransformUtility.GetRangeEnd(xRadius, pX, maxX); + + if (bottom <= top || right <= left) + { + continue; + } + + Vector4 sum = Vector4.Zero; + for (int yK = top; yK <= bottom; yK++) + { + float yWeight = sampler.GetValue(yK - pY); + + for (int xK = left; xK <= right; xK++) + { + float xWeight = sampler.GetValue(xK - pX); + + Vector4 current = sourceBuffer.GetElementUnsafe(xK, yK).ToScaledVector4(); + Numerics.Premultiply(ref current); + sum += current * xWeight * yWeight; + } + } + + span[x] = sum; + } + + Numerics.UnPremultiply(span); + PixelOperations.Instance.FromVector4Destructive( + this.configuration, span, - x, - ref yKernelSpanRef, - ref xKernelSpanRef, - this.radialExtents, - this.maxSourceExtents); + rowSpan, + PixelConversionModifiers.Scale); } + } + + [ExcludeFromCodeCoverage] + [MethodImpl(InliningOptions.ShortMethod)] + public void InvokeMacOSX(in RowInterval rows, Span span) + { + Matrix4x4 matrix = this.matrix; + TResampler sampler = this.sampler; + float yRadius = this.yRadius; + float xRadius = this.xRadius; + int maxY = this.source.Height - 1; + int maxX = this.source.Width - 1; - PixelOperations.Instance.FromVector4Destructive( - this.configuration, - span, - this.destination.GetPixelRowSpan(y)); + Buffer2D sourceBuffer = this.source.PixelBuffer; + + for (int y = rows.Min; y < rows.Max; y++) + { + Span rowSpan = this.destination.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4( + this.configuration, + rowSpan, + span, + PixelConversionModifiers.Scale); + + for (int x = 0; x < span.Length; x++) + { + Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); + float pY = point.Y; + float pX = point.X; + + int top = LinearTransformUtility.GetRangeStart(yRadius, pY, maxY); + int bottom = LinearTransformUtility.GetRangeEnd(yRadius, pY, maxY); + int left = LinearTransformUtility.GetRangeStart(xRadius, pX, maxX); + int right = LinearTransformUtility.GetRangeEnd(xRadius, pX, maxX); + + if (bottom <= top || right <= left) + { + continue; + } + + Vector4 sum = Vector4.Zero; + for (int yK = top; yK <= bottom; yK++) + { + float yWeight = sampler.GetValue(yK - pY); + + for (int xK = left; xK <= right; xK++) + { + float xWeight = sampler.GetValue(xK - pX); + + Vector4 current = sourceBuffer.GetElementUnsafe(xK, yK).ToScaledVector4(); + Numerics.Premultiply(ref current); + sum += current * xWeight * yWeight; + } + } + + Numerics.UnPremultiply(ref sum); + span[x] = sum; + } + + PixelOperations.Instance.FromVector4Destructive( + this.configuration, + span, + rowSpan, + PixelConversionModifiers.Scale); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs index 66f885f23a..a67ed92a56 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs @@ -13,7 +13,7 @@ using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Points to a collection of of weights allocated in . + /// Points to a collection of weights allocated in . /// internal readonly unsafe struct ResizeKernel { @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - /// Gets the the length of the kernel. + /// Gets the length of the kernel. /// public int Length { diff --git a/tests/ImageSharp.Benchmarks/Processing/Rotate.cs b/tests/ImageSharp.Benchmarks/Processing/Rotate.cs index 107c47f062..1b8aed0068 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Rotate.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Rotate.cs @@ -21,21 +21,19 @@ namespace SixLabors.ImageSharp.Benchmarks.Processing } } -// #### 21th February 2020 #### +// #### 2021-04-06 #### // -// BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363 -// Intel Core i7-8650U CPU 1.90GHz(Kaby Lake R), 1 CPU, 8 logical and 4 physical cores -// .NET Core SDK = 3.1.101 +// BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 +// Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +// .NET Core SDK=5.0.201 +// [Host] : .NET Core 5.0.4 (CoreCLR 5.0.421.11614, CoreFX 5.0.421.11614), X64 RyuJIT +// Job-HQWHDJ : .NET Framework 4.8 (4.8.4341.0), X64 RyuJIT +// Job-RPXLFC : .NET Core 2.1.26 (CoreCLR 4.6.29812.02, CoreFX 4.6.29812.01), X64 RyuJIT +// Job-YMSKIM : .NET Core 3.1.13 (CoreCLR 4.700.21.11102, CoreFX 4.700.21.11602), X64 RyuJIT // -// [Host] : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT -// Job-HOGSNT : .NET Framework 4.8 (4.8.4121.0), X64 RyuJIT -// Job-FKDHXC : .NET Core 2.1.15 (CoreCLR 4.6.28325.01, CoreFX 4.6.28327.02), X64 RyuJIT -// Job-ODABAZ : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT // -// IterationCount=3 LaunchCount=1 WarmupCount=3 -// -// | Method | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | -// |--------- |-------------- |---------:|---------:|---------:|------:|------:|------:|----------:| -// | DoRotate | .NET 4.7.2 | 28.77 ms | 3.304 ms | 0.181 ms | - | - | - | 6.5 KB | -// | DoRotate | .NET Core 2.1 | 16.27 ms | 1.044 ms | 0.057 ms | - | - | - | 5.25 KB | -// | DoRotate | .NET Core 3.1 | 17.12 ms | 4.352 ms | 0.239 ms | - | - | - | 6.57 KB | +// | Method | Job | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |--------- |----------- |-------------- |---------:|---------:|---------:|------:|------:|------:|----------:| +// | DoRotate | Job-BAUEPW | .NET 4.7.2 | 30.73 ms | 0.397 ms | 0.331 ms | - | - | - | 6.75 KB | +// | DoRotate | Job-SNWMCN | .NET Core 2.1 | 16.31 ms | 0.317 ms | 0.352 ms | - | - | - | 5.25 KB | +// | DoRotate | Job-MRMBJZ | .NET Core 3.1 | 12.21 ms | 0.239 ms | 0.245 ms | - | - | - | 6.61 KB | diff --git a/tests/ImageSharp.Benchmarks/Processing/Skew.cs b/tests/ImageSharp.Benchmarks/Processing/Skew.cs index b77f0dcd6b..1c92b9f3c5 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Skew.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Skew.cs @@ -21,21 +21,19 @@ namespace SixLabors.ImageSharp.Benchmarks.Processing } } -// #### 21th February 2020 #### +// #### 2021-04-06 #### // -// BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363 -// Intel Core i7-8650U CPU 1.90GHz(Kaby Lake R), 1 CPU, 8 logical and 4 physical cores -// .NET Core SDK = 3.1.101 +// BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 +// Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +// .NET Core SDK=5.0.201 +// [Host] : .NET Core 5.0.4 (CoreCLR 5.0.421.11614, CoreFX 5.0.421.11614), X64 RyuJIT +// Job-HQWHDJ : .NET Framework 4.8 (4.8.4341.0), X64 RyuJIT +// Job-RPXLFC : .NET Core 2.1.26 (CoreCLR 4.6.29812.02, CoreFX 4.6.29812.01), X64 RyuJIT +// Job-YMSKIM : .NET Core 3.1.13 (CoreCLR 4.700.21.11102, CoreFX 4.700.21.11602), X64 RyuJIT // -// [Host] : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT -// Job-VKKTMF : .NET Framework 4.8 (4.8.4121.0), X64 RyuJIT -// Job-KTVRKR : .NET Core 2.1.15 (CoreCLR 4.6.28325.01, CoreFX 4.6.28327.02), X64 RyuJIT -// Job-EONWDB : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT // -// IterationCount=3 LaunchCount=1 WarmupCount=3 -// -// | Method | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | -// |------- |-------------- |---------:|----------:|---------:|------:|------:|------:|----------:| -// | DoSkew | .NET 4.7.2 | 24.60 ms | 33.971 ms | 1.862 ms | - | - | - | 6.5 KB | -// | DoSkew | .NET Core 2.1 | 12.13 ms | 2.256 ms | 0.124 ms | - | - | - | 5.21 KB | -// | DoSkew | .NET Core 3.1 | 12.83 ms | 1.442 ms | 0.079 ms | - | - | - | 6.57 KB | +// | Method | Job | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |------- |----------- |-------------- |----------:|----------:|----------:|------:|------:|------:|----------:| +// | DoSkew | Job-YEGFRQ | .NET 4.7.2 | 23.563 ms | 0.0731 ms | 0.0570 ms | - | - | - | 6.75 KB | +// | DoSkew | Job-HZHOGR | .NET Core 2.1 | 13.700 ms | 0.2727 ms | 0.5122 ms | - | - | - | 5.25 KB | +// | DoSkew | Job-LTEUKY | .NET Core 3.1 | 9.971 ms | 0.0254 ms | 0.0225 ms | - | - | - | 6.61 KB | diff --git a/tests/ImageSharp.Tests/Helpers/RuntimeEnvironmentTests.cs b/tests/ImageSharp.Tests/Helpers/RuntimeEnvironmentTests.cs new file mode 100644 index 0000000000..8074b8b150 --- /dev/null +++ b/tests/ImageSharp.Tests/Helpers/RuntimeEnvironmentTests.cs @@ -0,0 +1,41 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.InteropServices; +using Xunit; + +#pragma warning disable IDE0022 // Use expression body for methods +namespace SixLabors.ImageSharp.Tests.Helpers +{ + public class RuntimeEnvironmentTests + { + [Fact] + public void CanDetectNetCore() + { +#if NET5_0_OR_GREATER + Assert.False(RuntimeEnvironment.IsNetCore); +#elif NETCOREAPP + Assert.True(RuntimeEnvironment.IsNetCore); +#else + Assert.False(RuntimeEnvironment.IsNetCore); +#endif + } + + [Fact] + public void CanDetectOSPlatform() + { + if (TestEnvironment.IsLinux) + { + Assert.True(RuntimeEnvironment.IsOSPlatform(OSPlatform.Linux)); + } + else if (TestEnvironment.IsOSX) + { + Assert.True(RuntimeEnvironment.IsOSPlatform(OSPlatform.OSX)); + } + else if (TestEnvironment.IsWindows) + { + Assert.True(RuntimeEnvironment.IsOSPlatform(OSPlatform.Windows)); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs index 379f74d094..49a443d927 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs @@ -4,7 +4,6 @@ using System; using System.Numerics; using System.Reflection; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; @@ -18,9 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public class AffineTransformTests { private readonly ITestOutputHelper output; - - // 1 byte difference on one color component. - private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0134F, 3); + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.033F, 3); /// /// angleDeg, sx, sy, tx, ty diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index fcde6273f5..8a038a6912 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -174,7 +174,7 @@ namespace SixLabors.ImageSharp.Tests appendPixelTypeToFileName, appendSourceFileOrDescription); - encoder = encoder ?? TestEnvironment.GetReferenceEncoder(path); + encoder ??= TestEnvironment.GetReferenceEncoder(path); using (FileStream stream = File.OpenWrite(path)) { diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ImageSharpPngEncoderWithDefaultConfiguration.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ImageSharpPngEncoderWithDefaultConfiguration.cs new file mode 100644 index 0000000000..f0a01e45e8 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/ImageSharpPngEncoderWithDefaultConfiguration.cs @@ -0,0 +1,94 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs +{ + /// + /// A Png encoder that uses the ImageSharp core encoder but the default configuration. + /// This allows encoding under environments with restricted memory. + /// + public sealed class ImageSharpPngEncoderWithDefaultConfiguration : IImageEncoder, IPngEncoderOptions + { + /// + public PngBitDepth? BitDepth { get; set; } + + /// + public PngColorType? ColorType { get; set; } + + /// + public PngFilterMethod? FilterMethod { get; set; } + + /// + public PngCompressionLevel CompressionLevel { get; set; } = PngCompressionLevel.DefaultCompression; + + /// + public int TextCompressionThreshold { get; set; } = 1024; + + /// + public float? Gamma { get; set; } + + /// + public IQuantizer Quantizer { get; set; } + + /// + public byte Threshold { get; set; } = byte.MaxValue; + + /// + public PngInterlaceMode? InterlaceMethod { get; set; } + + /// + public PngChunkFilter? ChunkFilter { get; set; } + + /// + public bool IgnoreMetadata { get; set; } + + /// + public PngTransparentColorMode TransparentColorMode { get; set; } + + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + public void Encode(Image image, Stream stream) + where TPixel : unmanaged, IPixel + { + Configuration configuration = Configuration.Default; + MemoryAllocator allocator = configuration.MemoryAllocator; + + using var encoder = new PngEncoderCore(allocator, configuration, new PngEncoderOptions(this)); + encoder.Encode(image, stream); + } + + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + /// The token to monitor for cancellation requests. + /// A representing the asynchronous operation. + public async Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + Configuration configuration = Configuration.Default; + MemoryAllocator allocator = configuration.MemoryAllocator; + + // The introduction of a local variable that refers to an object the implements + // IDisposable means you must use async/await, where the compiler generates the + // state machine and a continuation. + using var encoder = new PngEncoderCore(allocator, configuration, new PngEncoderOptions(this)); + await encoder.EncodeAsync(image, stream, cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 6e204e2d48..ea5b2f6652 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -57,10 +57,10 @@ namespace SixLabors.ImageSharp.Tests new GifConfigurationModule(), new TgaConfigurationModule()); - // Magick codecs should work on all platforms - IImageEncoder pngEncoder = IsWindows ? (IImageEncoder)SystemDrawingReferenceEncoder.Png : new PngEncoder(); + IImageEncoder pngEncoder = IsWindows ? (IImageEncoder)SystemDrawingReferenceEncoder.Png : new ImageSharpPngEncoderWithDefaultConfiguration(); IImageEncoder bmpEncoder = IsWindows ? (IImageEncoder)SystemDrawingReferenceEncoder.Bmp : new BmpEncoder(); + // Magick codecs should work on all platforms cfg.ConfigureCodecs( PngFormat.Instance, MagickReferenceDecoder.Instance, diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs index cb8a0df42f..587b274a10 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs @@ -26,6 +26,8 @@ namespace SixLabors.ImageSharp.Tests private static readonly Lazy RunsOnCiLazy = new Lazy(() => bool.TryParse(Environment.GetEnvironmentVariable("CI"), out bool isCi) && isCi); + private static readonly Lazy RunsWithCodeCoverageLazy = new Lazy(() => bool.TryParse(Environment.GetEnvironmentVariable("codecov"), out bool isCodeCov) && isCodeCov); + private static readonly Lazy NetCoreVersionLazy = new Lazy(GetNetCoreVersion); static TestEnvironment() => PrepareRemoteExecutor(); @@ -42,6 +44,11 @@ namespace SixLabors.ImageSharp.Tests /// internal static bool RunsOnCI => RunsOnCiLazy.Value; + /// + /// Gets a value indicating whether test execution is running with code coverage testing enabled. + /// + internal static bool RunsWithCodeCoverage => RunsWithCodeCoverageLazy.Value; + internal static string SolutionDirectoryFullPath => SolutionDirectoryFullPathLazy.Value; private static readonly FileInfo TestAssemblyFile = diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 073db1efed..1c5aedd9b7 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -34,18 +34,16 @@ namespace SixLabors.ImageSharp.Tests bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true, IImageEncoder encoder = null) - { - image.DebugSave( + => image.DebugSave( provider, (object)testOutputDetails, extension, appendPixelTypeToFileName, appendSourceFileOrDescription, encoder); - } /// - /// Saves the image only when not running in the CI server. + /// Saves the image for debugging purpose. /// /// The image. /// The image provider. @@ -64,12 +62,11 @@ namespace SixLabors.ImageSharp.Tests bool appendSourceFileOrDescription = true, IImageEncoder encoder = null) { - if (TestEnvironment.RunsOnCI) + if (TestEnvironment.RunsWithCodeCoverage) { return image; } - // We are running locally then we want to save it out provider.Utility.SaveTestOutputFile( image, extension, @@ -86,12 +83,10 @@ namespace SixLabors.ImageSharp.Tests IImageEncoder encoder, FormattableString testOutputDetails, bool appendPixelTypeToFileName = true) - { - image.DebugSave(provider, encoder, (object)testOutputDetails, appendPixelTypeToFileName); - } + => image.DebugSave(provider, encoder, (object)testOutputDetails, appendPixelTypeToFileName); /// - /// Saves the image only when not running in the CI server. + /// Saves the image for debugging purpose. /// /// The image /// The image provider @@ -104,19 +99,11 @@ namespace SixLabors.ImageSharp.Tests IImageEncoder encoder, object testOutputDetails = null, bool appendPixelTypeToFileName = true) - { - if (TestEnvironment.RunsOnCI) - { - return; - } - - // We are running locally then we want to save it out - provider.Utility.SaveTestOutputFile( + => provider.Utility.SaveTestOutputFile( image, encoder: encoder, testOutputDetails: testOutputDetails, appendPixelTypeToFileName: appendPixelTypeToFileName); - } public static Image DebugSaveMultiFrame( this Image image, @@ -126,17 +113,17 @@ namespace SixLabors.ImageSharp.Tests bool appendPixelTypeToFileName = true) where TPixel : unmanaged, IPixel { - if (TestEnvironment.RunsOnCI) + if (TestEnvironment.RunsWithCodeCoverage) { return image; } - // We are running locally then we want to save it out provider.Utility.SaveTestOutputFileMultiFrame( image, extension, testOutputDetails: testOutputDetails, appendPixelTypeToFileName: appendPixelTypeToFileName); + return image; } @@ -149,15 +136,13 @@ namespace SixLabors.ImageSharp.Tests bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true) where TPixel : unmanaged, IPixel - { - return image.CompareToReferenceOutput( + => image.CompareToReferenceOutput( provider, (object)testOutputDetails, extension, grayscale, appendPixelTypeToFileName, appendSourceFileOrDescription); - } /// /// Compares the image against the expected Reference output, throws an exception if the images are not similar enough. @@ -181,8 +166,7 @@ namespace SixLabors.ImageSharp.Tests bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true) where TPixel : unmanaged, IPixel - { - return CompareToReferenceOutput( + => CompareToReferenceOutput( image, ImageComparer.Tolerant(), provider, @@ -191,7 +175,6 @@ namespace SixLabors.ImageSharp.Tests grayscale, appendPixelTypeToFileName, appendSourceFileOrDescription); - } public static Image CompareToReferenceOutput( this Image image, @@ -202,15 +185,13 @@ namespace SixLabors.ImageSharp.Tests bool grayscale = false, bool appendPixelTypeToFileName = true) where TPixel : unmanaged, IPixel - { - return image.CompareToReferenceOutput( + => image.CompareToReferenceOutput( comparer, provider, (object)testOutputDetails, extension, grayscale, appendPixelTypeToFileName); - } /// /// Compares the image against the expected Reference output, throws an exception if the images are not similar enough. @@ -263,8 +244,7 @@ namespace SixLabors.ImageSharp.Tests bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true) where TPixel : unmanaged, IPixel - { - return image.CompareFirstFrameToReferenceOutput( + => image.CompareFirstFrameToReferenceOutput( comparer, provider, (object)testOutputDetails, @@ -272,7 +252,6 @@ namespace SixLabors.ImageSharp.Tests grayscale, appendPixelTypeToFileName, appendSourceFileOrDescription); - } public static Image CompareFirstFrameToReferenceOutput( this Image image, @@ -508,9 +487,7 @@ namespace SixLabors.ImageSharp.Tests ITestImageProvider provider, IImageDecoder referenceDecoder = null) where TPixel : unmanaged, IPixel - { - return CompareToOriginal(image, provider, ImageComparer.Tolerant(), referenceDecoder); - } + => CompareToOriginal(image, provider, ImageComparer.Tolerant(), referenceDecoder); public static Image CompareToOriginal( this Image image, @@ -584,14 +561,12 @@ namespace SixLabors.ImageSharp.Tests bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true) where TPixel : unmanaged, IPixel - { - provider.VerifyOperation( + => provider.VerifyOperation( ImageComparer.Tolerant(), operation, testOutputDetails, appendPixelTypeToFileName, appendSourceFileOrDescription); - } /// /// Utility method for doing the following in one step: @@ -606,14 +581,12 @@ namespace SixLabors.ImageSharp.Tests bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true) where TPixel : unmanaged, IPixel - { - provider.VerifyOperation( + => provider.VerifyOperation( comparer, operation, $"", appendPixelTypeToFileName, appendSourceFileOrDescription); - } /// /// Utility method for doing the following in one step: @@ -627,9 +600,7 @@ namespace SixLabors.ImageSharp.Tests bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true) where TPixel : unmanaged, IPixel - { - provider.VerifyOperation(operation, $"", appendPixelTypeToFileName, appendSourceFileOrDescription); - } + => provider.VerifyOperation(operation, $"", appendPixelTypeToFileName, appendSourceFileOrDescription); /// /// Loads the expected image with a reference decoder + compares it to . diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs index 67f11e8978..84b9297295 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -8,7 +8,6 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; @@ -85,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [InlineData("lol/foo.png", typeof(PngEncoder))] + [InlineData("lol/foo.png", typeof(ImageSharpPngEncoderWithDefaultConfiguration))] [InlineData("lol/Rofl.bmp", typeof(BmpEncoder))] [InlineData("lol/Baz.JPG", typeof(JpegEncoder))] [InlineData("lol/Baz.gif", typeof(GifEncoder))] diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png index ec3bfb5d11..49c7795fe5 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57376aa4446fc2d034d2a0cb627163ac416d1b6768b063b2c11ccf8517443bda -size 10135 +oid sha256:2ef489dc0837b382ad7c7ead6b7c7042dfbfba39902d4cc81b5f3805d5b03967 +size 9175 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50__original.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50__original.png index bdad18d1dc..a6ff73cf83 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50__original.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50__original.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c83b59471a50df9e1d7b8f0e35c50aa417cd3be1730d6369f47f5cc99b87cef -size 6405 +oid sha256:99d6c1d6b092a2feba2aebe2e09c521c3cc9682f3d748927cdc3cbaa38448b28 +size 710 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png index 526096e7c0..a909194b0b 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9f149093c5d9d487f3e16cb59e6807ea6ce581b5c307e57aeb4edaf921de9ca -size 15138 +oid sha256:04a3b84c668758b586f4d998f080ef96b09f726b0298b28f5dd3d739b0e90744 +size 13138 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png index 526096e7c0..a909194b0b 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9f149093c5d9d487f3e16cb59e6807ea6ce581b5c307e57aeb4edaf921de9ca -size 15138 +oid sha256:04a3b84c668758b586f4d998f080ef96b09f726b0298b28f5dd3d739b0e90744 +size 13138 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png index b3439a5c88..e248b6d918 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c7467702a784807a3a5813a75d5f643655ed5ad427e978d6ee079da67b05961 -size 15363 +oid sha256:6ea7ca66c31474c0bb9673a0d85c1c7465e387ebabf4e2d1e8f9daebfc7c8f34 +size 13956 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png index 4622adab47..5c81a5f5da 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a836c63c0912f69683bc9c8f59687b11e6b84f257816a01ff979ad0b6f4ab656 -size 19059 +oid sha256:6dd98ac441f3c20ea999f058c7b21601d5981d46e9b77709c25f2930a64edb93 +size 17148 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png index 753764631b..1647aae60d 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05de30dc282a0b8a096a476f689a1d2b6bb298098692a2f665c59c3d14902aa6 -size 20426 +oid sha256:d5c4772d9b9dc57c4b5d47450ec9d02d96e40656cf2015f171b5425945f8a598 +size 18726 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png index c0840e5f76..3949197248 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8892179d8edf96583900048bdd895eff24e57be0105e91efafe1de971414db0e -size 22457 +oid sha256:bb025c4470cec1b0d6544924e46b84cbdb90d75da5d0f879f2c7d7ec9875dee2 +size 20574 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png index 094777eec3..da8413be52 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f351ec6ae6df56537e383494984c2fbcf35a66c44a6ce53d6fd8d6d74a330f3 -size 15342 +oid sha256:c4abaa06827cb779026f8fbb655692bdd8adab37ae5b00c3ae18ebea456eb8d9 +size 13459 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png index c580f0cffd..5bdf261405 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b0fb38dbdded32a1518d62ac79f4aa88133aaddad947f23c1066dc33d6938e0b -size 15372 +oid sha256:3435ade8f7988779280820342e16881b049f717735d2218ac5a971a1bd807db1 +size 13448 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png index 4c0bb37bc3..0e2dbf2565 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0be42a5fd4ff10680af74a99ed1e0561ae38181f4efe4641bd63891222dcdf3c -size 15283 +oid sha256:f0098aa45e820544dd16a58396fa70860886b7d79900916ed97376a8328a5ff2 +size 13367 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png index 7527157d50..27ed945dc8 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1eabaf35e0dda3eccd5e8866ddd65e0c45557c8d5cc29423a99e2f377ee1bfa9 -size 16271 +oid sha256:7729495277b80a42e24dd8f40cdd8a280443575710fb4e199e4871c28b558271 +size 14253 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png index 1ee2a15ff6..90c47e96d7 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ab9bea8f45e7d29d7a8c5e3d0182c0f3f3aa7014aa358883dee53db6dfeb3f7 -size 14076 +oid sha256:a2304c234b93bdabaa018263dec70e62090ad1bbb7005ef62643b88600a863fb +size 12157 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png index d3b8938091..581b229500 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea452bc46c508f6990870a34d908224a58aa350c4ccebabd4fa6ba138e8034a0 -size 18383 +oid sha256:54b0da9646b7f4cf83df784d69dfbec48e0bdc1788d70a9872817543f72f57c1 +size 16829 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png index bfb9ab5edf..c04521ebc7 100644 --- a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png +++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c83df7a2f70aec8f150799055ce42db09568b47b95216c91a79233ce69381d5 -size 191563 +oid sha256:233d8c6c9e101dddf5d210d47c7a20807f8f956738289068ea03b774258ef8c6 +size 182754 From 06f9e2483a41f2cfc0313352976d76b05b34af21 Mon Sep 17 00:00:00 2001 From: Clinton Ingram Date: Thu, 15 Apr 2021 15:52:25 -0700 Subject: [PATCH 21/25] Update PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4be3511650..8709e1318d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,7 +2,7 @@ - [ ] I have written a descriptive pull-request title - [ ] I have verified that there are no overlapping [pull-requests](https://github.com/SixLabors/ImageSharp/pulls) open -- [ ] I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules :cop:. +- [ ] I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules :cop:. - [ ] I have provided test coverage for my change (where applicable) ### Description From 14954cf5f240c7a12a4ddeaae17058abe930d2db Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 16 Apr 2021 17:42:05 +0100 Subject: [PATCH 22/25] Update to latest config/ruleset --- .editorconfig | 143 +++++++++++++++++++++++------------------- shared-infrastructure | 2 +- 2 files changed, 80 insertions(+), 65 deletions(-) diff --git a/.editorconfig b/.editorconfig index 83670fa830..03036f8a53 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,5 @@ -# Version: 1.6.2 (Using https://semver.org/) -# Updated: 2020-11-02 +# Version: 2.1.0 (Using https://semver.org/) +# Updated: 2021-03-03 # See https://github.com/RehanSaeed/EditorConfig/releases for release notes. # See https://github.com/RehanSaeed/EditorConfig for updates to this file. # See http://EditorConfig.org for more information about .editorconfig files. @@ -60,87 +60,84 @@ indent_size = 2 [*.{cmd,bat}] end_of_line = crlf +# Bash Files +[*.sh] +end_of_line = lf + # Makefiles [Makefile] indent_style = tab ########################################## -# File Header (Uncomment to support file headers) -# https://docs.microsoft.com/visualstudio/ide/reference/add-file-header +# Default .NET Code Style Severities +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/configuration-options#scope ########################################## -# [*.{cs,csx,cake,vb,vbx,tt,ttinclude}] -file_header_template = Copyright (c) Six Labors.\nLicensed under the Apache License, Version 2.0. - -# SA1636: File header copyright text should match -# Justification: .editorconfig supports file headers. If this is changed to a value other than "none", a stylecop.json file will need to added to the project. -# dotnet_diagnostic.SA1636.severity = none +[*.{cs,csx,cake,vb,vbx}] +# Default Severity for all .NET Code Style rules below +dotnet_analyzer_diagnostic.severity = warning ########################################## -# .NET Language Conventions -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions +# Language Rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules ########################################## -# .NET Code Style Settings -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#net-code-style-settings +# .NET Style Rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#net-style-rules [*.{cs,csx,cake,vb,vbx}] # "this." and "Me." qualifiers -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#this-and-me dotnet_style_qualification_for_field = true:warning dotnet_style_qualification_for_property = true:warning dotnet_style_qualification_for_method = true:warning dotnet_style_qualification_for_event = true:warning # Language keywords instead of framework type names for type references -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#language-keywords dotnet_style_predefined_type_for_locals_parameters_members = true:warning dotnet_style_predefined_type_for_member_access = true:warning # Modifier preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#normalize-modifiers dotnet_style_require_accessibility_modifiers = always:warning csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:warning dotnet_style_readonly_field = true:warning # Parentheses preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parentheses-preferences dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_operators = always_for_clarity:suggestion # Expression-level preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences dotnet_style_object_initializer = true:warning dotnet_style_collection_initializer = true:warning dotnet_style_explicit_tuple_names = true:warning dotnet_style_prefer_inferred_tuple_names = true:warning dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning dotnet_style_prefer_auto_properties = true:warning -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning dotnet_style_prefer_conditional_expression_over_assignment = false:suggestion +dotnet_diagnostic.IDE0045.severity = suggestion dotnet_style_prefer_conditional_expression_over_return = false:suggestion +dotnet_diagnostic.IDE0046.severity = suggestion dotnet_style_prefer_compound_assignment = true:warning +dotnet_style_prefer_simplified_interpolation = true:warning +dotnet_style_prefer_simplified_boolean_expressions = true:warning # Null-checking preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#null-checking-preferences dotnet_style_coalesce_expression = true:warning dotnet_style_null_propagation = true:warning -# Parameter preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parameter-preferences -dotnet_code_quality_unused_parameters = all:warning -# More style options (Undocumented) -# https://github.com/MicrosoftDocs/visualstudio-docs/issues/3641 +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning +# File header preferences +file_header_template = Copyright (c) Six Labors.\nLicensed under the Apache License, Version 2.0. +# SA1636: File header copyright text should match +# Justification: .editorconfig supports file headers. If this is changed to a value other than "none", a stylecop.json file will need to added to the project. +# dotnet_diagnostic.SA1636.severity = none + +# Undocumented dotnet_style_operator_placement_when_wrapping = end_of_line -# https://github.com/dotnet/roslyn/pull/40070 -dotnet_style_prefer_simplified_interpolation = true:warning -# C# Code Style Settings -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-code-style-settings +# C# Style Rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#c-style-rules [*.{cs,csx,cake}] -# Implicit and explicit types -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#implicit-and-explicit-types +# 'var' preferences csharp_style_var_for_built_in_types = never csharp_style_var_when_type_is_apparent = true:warning csharp_style_var_elsewhere = false:warning # Expression-bodied members -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-bodied-members csharp_style_expression_bodied_methods = true:warning csharp_style_expression_bodied_constructors = true:warning csharp_style_expression_bodied_operators = true:warning @@ -149,47 +146,64 @@ csharp_style_expression_bodied_indexers = true:warning csharp_style_expression_bodied_accessors = true:warning csharp_style_expression_bodied_lambdas = true:warning csharp_style_expression_bodied_local_functions = true:warning -# Pattern matching -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#pattern-matching +# Pattern matching preferences csharp_style_pattern_matching_over_is_with_cast_check = true:warning csharp_style_pattern_matching_over_as_with_null_check = true:warning -# Inlined variable declarations -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#inlined-variable-declarations -csharp_style_inlined_variable_declaration = true:warning +csharp_style_prefer_switch_expression = true:warning +csharp_style_prefer_pattern_matching = true:warning +csharp_style_prefer_not_pattern = true:warning # Expression-level preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences +csharp_style_inlined_variable_declaration = true:warning csharp_prefer_simple_default_expression = true:warning +csharp_style_pattern_local_over_anonymous_function = true:warning +csharp_style_deconstructed_variable_declaration = true:warning +csharp_style_prefer_index_operator = true:warning +csharp_style_prefer_range_operator = true:warning +csharp_style_implicit_object_creation_when_type_is_apparent = true:warning # "Null" checking preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-null-checking-preferences csharp_style_throw_expression = true:warning csharp_style_conditional_delegate_call = true:warning # Code block preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#code-block-preferences csharp_prefer_braces = true:warning -# Unused value preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#unused-value-preferences -csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion -csharp_style_unused_value_assignment_preference = discard_variable:suggestion -# Index and range preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#index-and-range-preferences -csharp_style_prefer_index_operator = true:warning -csharp_style_prefer_range_operator = true:warning -# Miscellaneous preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#miscellaneous-preferences -csharp_style_deconstructed_variable_declaration = true:warning -csharp_style_pattern_local_over_anonymous_function = true:warning +csharp_prefer_simple_using_statement = true:suggestion +dotnet_diagnostic.IDE0063.severity = suggestion +# 'using' directive preferences csharp_using_directive_placement = outside_namespace:warning +# Modifier preferences csharp_prefer_static_local_function = true:warning -csharp_prefer_simple_using_statement = true:suggestion ########################################## -# .NET Formatting Conventions -# https://docs.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference#formatting-conventions +# Unnecessary Code Rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/unnecessary-code-rules ########################################## -# Organize usings -# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#organize-using-directives +# .NET Unnecessary code rules +[*.{cs,csx,cake,vb,vbx}] +dotnet_code_quality_unused_parameters = all:warning +dotnet_remove_unnecessary_suppression_exclusions = none:warning + +# C# Unnecessary code rules +[*.{cs,csx,cake}] +csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion +dotnet_diagnostic.IDE0058.severity = suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +dotnet_diagnostic.IDE0059.severity = suggestion + +########################################## +# Formatting Rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules +########################################## + +# .NET formatting rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#net-formatting-rules +[*.{cs,csx,cake,vb,vbx}] +# Organize using directives dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# C# formatting rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#c-formatting-rules +[*.{cs,csx,cake}] # Newline options # https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#new-line-options csharp_new_line_before_open_brace = all @@ -231,14 +245,14 @@ csharp_space_around_declaration_statements = false csharp_space_before_open_square_brackets = false csharp_space_between_empty_square_brackets = false csharp_space_between_square_brackets = false -# Wrapping options +# Wrap options # https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#wrap-options csharp_preserve_single_line_statements = false csharp_preserve_single_line_blocks = true ########################################## -# .NET Naming Conventions -# https://docs.microsoft.com/visualstudio/ide/editorconfig-naming-conventions +# .NET Naming Rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/naming-rules ########################################## [*.{cs,csx,cake,vb,vbx}] @@ -261,8 +275,9 @@ dotnet_naming_style.prefix_type_parameters_with_t_style.capitalization = pascal_ dotnet_naming_style.prefix_type_parameters_with_t_style.required_prefix = T # disallowed_style - Anything that has this style applied is marked as disallowed dotnet_naming_style.disallowed_style.capitalization = pascal_case -dotnet_naming_style.disallowed_style.required_prefix = ____RULE_VIOLATION____ -dotnet_naming_style.disallowed_style.required_suffix = ____RULE_VIOLATION____ +# Disabled while we investigate compatibility with VS 16.10 +#dotnet_naming_style.disallowed_style.required_prefix = ____RULE_VIOLATION____ +#dotnet_naming_style.disallowed_style.required_suffix = ____RULE_VIOLATION____ # internal_error_style - This style should never occur... if it does, it indicates a bug in file or in the parser using the file dotnet_naming_style.internal_error_style.capitalization = pascal_case dotnet_naming_style.internal_error_style.required_prefix = ____INTERNAL_ERROR____ diff --git a/shared-infrastructure b/shared-infrastructure index 9b1179f0eb..462f789c52 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 9b1179f0ebe6a4dfed998252b860fa07fee54363 +Subproject commit 462f789c52809728505833d101b9a96022e0fc3b From a4d05f5fd2d5cd3a6e8845bc609a191a9aa75a85 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 16 Apr 2021 22:17:57 +0100 Subject: [PATCH 23/25] Add faster InnerLoop build configurations --- Directory.Build.props | 9 ++++ ImageSharp.sln | 54 +++++++++++++++++++ src/ImageSharp/ImageSharp.csproj | 6 +++ .../ImageSharp.Benchmarks.csproj | 15 +++++- .../ImageSharp.Tests.ProfilingSandbox.csproj | 15 +++++- .../app.config | 6 +-- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 15 +++++- 7 files changed, 114 insertions(+), 6 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 3a133efe7a..3df93fcd40 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -18,4 +18,13 @@ + + + false + + + + true + + diff --git a/ImageSharp.sln b/ImageSharp.sln index 8dfab6033c..83208994cb 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -482,9 +482,15 @@ Global Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Debug-InnerLoop|Any CPU = Debug-InnerLoop|Any CPU + Debug-InnerLoop|x64 = Debug-InnerLoop|x64 + Debug-InnerLoop|x86 = Debug-InnerLoop|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 + Release-InnerLoop|Any CPU = Release-InnerLoop|Any CPU + Release-InnerLoop|x64 = Release-InnerLoop|x64 + Release-InnerLoop|x86 = Release-InnerLoop|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -493,48 +499,96 @@ Global {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug|x64.Build.0 = Debug|Any CPU {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug|x86.ActiveCfg = Debug|Any CPU {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug|x86.Build.0 = Debug|Any CPU + {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU + {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU + {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug-InnerLoop|x64.ActiveCfg = Debug-InnerLoop|Any CPU + {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug-InnerLoop|x64.Build.0 = Debug-InnerLoop|Any CPU + {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug-InnerLoop|x86.ActiveCfg = Debug-InnerLoop|Any CPU + {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug-InnerLoop|x86.Build.0 = Debug-InnerLoop|Any CPU {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|Any CPU.ActiveCfg = Release|Any CPU {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|Any CPU.Build.0 = Release|Any CPU {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|x64.ActiveCfg = Release|Any CPU {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|x64.Build.0 = Release|Any CPU {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|x86.ActiveCfg = Release|Any CPU {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|x86.Build.0 = Release|Any CPU + {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release-InnerLoop|Any CPU.ActiveCfg = Release-InnerLoop|Any CPU + {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release-InnerLoop|Any CPU.Build.0 = Release-InnerLoop|Any CPU + {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release-InnerLoop|x64.ActiveCfg = Release-InnerLoop|Any CPU + {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release-InnerLoop|x64.Build.0 = Release-InnerLoop|Any CPU + {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release-InnerLoop|x86.ActiveCfg = Release-InnerLoop|Any CPU + {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release-InnerLoop|x86.Build.0 = Release-InnerLoop|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|Any CPU.Build.0 = Debug|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|x64.ActiveCfg = Debug|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|x64.Build.0 = Debug|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|x86.ActiveCfg = Debug|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|x86.Build.0 = Debug|Any CPU + {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU + {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU + {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug-InnerLoop|x64.ActiveCfg = Debug-InnerLoop|x64 + {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug-InnerLoop|x64.Build.0 = Debug-InnerLoop|x64 + {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug-InnerLoop|x86.ActiveCfg = Debug-InnerLoop|x86 + {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug-InnerLoop|x86.Build.0 = Debug-InnerLoop|x86 {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|Any CPU.ActiveCfg = Release|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|Any CPU.Build.0 = Release|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|x64.ActiveCfg = Release|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|x64.Build.0 = Release|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|x86.ActiveCfg = Release|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|x86.Build.0 = Release|Any CPU + {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release-InnerLoop|Any CPU.ActiveCfg = Release-InnerLoop|Any CPU + {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release-InnerLoop|Any CPU.Build.0 = Release-InnerLoop|Any CPU + {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release-InnerLoop|x64.ActiveCfg = Release-InnerLoop|x64 + {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release-InnerLoop|x64.Build.0 = Release-InnerLoop|x64 + {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release-InnerLoop|x86.ActiveCfg = Release-InnerLoop|x86 + {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release-InnerLoop|x86.Build.0 = Release-InnerLoop|x86 {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|Any CPU.Build.0 = Debug|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|x64.ActiveCfg = Debug|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|x64.Build.0 = Debug|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|x86.ActiveCfg = Debug|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|x86.Build.0 = Debug|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|x64.ActiveCfg = Debug-InnerLoop|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|x64.Build.0 = Debug-InnerLoop|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|x86.ActiveCfg = Debug-InnerLoop|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|x86.Build.0 = Debug-InnerLoop|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|Any CPU.ActiveCfg = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|Any CPU.Build.0 = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x64.ActiveCfg = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x64.Build.0 = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.ActiveCfg = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.Build.0 = Release|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release-InnerLoop|Any CPU.ActiveCfg = Release-InnerLoop|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release-InnerLoop|Any CPU.Build.0 = Release-InnerLoop|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release-InnerLoop|x64.ActiveCfg = Release-InnerLoop|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release-InnerLoop|x64.Build.0 = Release-InnerLoop|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release-InnerLoop|x86.ActiveCfg = Release-InnerLoop|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release-InnerLoop|x86.Build.0 = Release-InnerLoop|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x64.ActiveCfg = Debug|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x64.Build.0 = Debug|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x86.ActiveCfg = Debug|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x86.Build.0 = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|x64.ActiveCfg = Debug-InnerLoop|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|x64.Build.0 = Debug-InnerLoop|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|x86.ActiveCfg = Debug-InnerLoop|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|x86.Build.0 = Debug-InnerLoop|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Release|Any CPU.Build.0 = Release|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x64.ActiveCfg = Release|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x64.Build.0 = Release|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x86.ActiveCfg = Release|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x86.Build.0 = Release|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release-InnerLoop|Any CPU.ActiveCfg = Release-InnerLoop|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release-InnerLoop|Any CPU.Build.0 = Release-InnerLoop|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release-InnerLoop|x64.ActiveCfg = Release-InnerLoop|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release-InnerLoop|x64.Build.0 = Release-InnerLoop|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release-InnerLoop|x86.ActiveCfg = Release-InnerLoop|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release-InnerLoop|x86.Build.0 = Release-InnerLoop|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 832d551fd7..510f34dc7d 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -12,6 +12,7 @@ $(RepositoryUrl) Image Resize Crop Gif Jpg Jpeg Bitmap Png Tga NetCore A new, fully featured, fully managed, cross-platform, 2D graphics API for .NET + Debug;Release;Release-InnerLoop;Debug-InnerLoop @@ -20,6 +21,11 @@ net5.0;netcoreapp3.1;netcoreapp2.1;netstandard2.1;netstandard2.0;netstandard1.3;net472 + + + netcoreapp3.1 + + netcoreapp3.1;netcoreapp2.1;netstandard2.1;netstandard2.0;netstandard1.3;net472 diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index d89cf79fc9..a146dc03ee 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -5,15 +5,28 @@ ImageSharp.Benchmarks Exe SixLabors.ImageSharp.Benchmarks - net5.0;netcoreapp3.1;netcoreapp2.1;net472 false false + Debug;Release;Release-InnerLoop;Debug-InnerLoop + + + + netcoreapp3.1 + + + + + net5.0;netcoreapp3.1;netcoreapp2.1;net472 + + + + diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj index 220780c58e..9159475326 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj @@ -8,13 +8,26 @@ false SixLabors.ImageSharp.Tests.ProfilingSandbox win7-x64 - net5.0;netcoreapp3.1;netcoreapp2.1;net472 SixLabors.ImageSharp.Tests.ProfilingSandbox.Program false false + Debug;Release;Release-InnerLoop;Debug-InnerLoop + + + + netcoreapp3.1 + + + + + net5.0;netcoreapp3.1;netcoreapp2.1;net472 + + + + diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/app.config b/tests/ImageSharp.Tests.ProfilingSandbox/app.config index 3328297a54..a74fa13156 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/app.config +++ b/tests/ImageSharp.Tests.ProfilingSandbox/app.config @@ -1,4 +1,4 @@ - + @@ -12,8 +12,8 @@ - + - \ No newline at end of file + diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index ebbe2cbdfa..b6482455e0 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -2,13 +2,26 @@ - net5.0;netcoreapp3.1;netcoreapp2.1;net472 True SixLabors.ImageSharp.Tests AnyCPU;x64;x86 SixLabors.ImageSharp.Tests + Debug;Release;Release-InnerLoop;Debug-InnerLoop + + + + netcoreapp3.1 + + + + + net5.0;netcoreapp3.1;netcoreapp2.1;net472 + + + + From c4d417ab6649e31e4cb0aef499b7bd3886d710fe Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 16 Apr 2021 22:24:59 +0100 Subject: [PATCH 24/25] Fix codecov test environment identification --- .../TestUtilities/TestEnvironment.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs index 587b274a10..b14c2bf782 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs @@ -24,10 +24,6 @@ namespace SixLabors.ImageSharp.Tests private static readonly Lazy SolutionDirectoryFullPathLazy = new Lazy(GetSolutionDirectoryFullPathImpl); - private static readonly Lazy RunsOnCiLazy = new Lazy(() => bool.TryParse(Environment.GetEnvironmentVariable("CI"), out bool isCi) && isCi); - - private static readonly Lazy RunsWithCodeCoverageLazy = new Lazy(() => bool.TryParse(Environment.GetEnvironmentVariable("codecov"), out bool isCodeCov) && isCodeCov); - private static readonly Lazy NetCoreVersionLazy = new Lazy(GetNetCoreVersion); static TestEnvironment() => PrepareRemoteExecutor(); @@ -42,12 +38,20 @@ namespace SixLabors.ImageSharp.Tests /// /// Gets a value indicating whether test execution runs on CI. /// - internal static bool RunsOnCI => RunsOnCiLazy.Value; +#if ENV_CI + internal static bool RunsOnCI => true; +#else + internal static bool RunsOnCI => false; +#endif /// /// Gets a value indicating whether test execution is running with code coverage testing enabled. /// - internal static bool RunsWithCodeCoverage => RunsWithCodeCoverageLazy.Value; +#if ENV_CODECOV + internal static bool RunsWithCodeCoverage => true; +#else + internal static bool RunsWithCodeCoverage => false; +#endif internal static string SolutionDirectoryFullPath => SolutionDirectoryFullPathLazy.Value; @@ -66,14 +70,14 @@ namespace SixLabors.ImageSharp.Tests } catch (Exception ex) { - throw new Exception( + throw new DirectoryNotFoundException( $"Unable to find ImageSharp solution directory from {TestAssemblyFile} because of {ex.GetType().Name}!", ex); } if (directory == null) { - throw new Exception($"Unable to find ImageSharp solution directory from {TestAssemblyFile}!"); + throw new DirectoryNotFoundException($"Unable to find ImageSharp solution directory from {TestAssemblyFile}!"); } } From 9f0515f27b4032106e45c6ecb266126e4e537650 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 17 Apr 2021 22:21:18 +0100 Subject: [PATCH 25/25] Update shared-infrastructure --- shared-infrastructure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-infrastructure b/shared-infrastructure index 462f789c52..41fff7bf7d 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 462f789c52809728505833d101b9a96022e0fc3b +Subproject commit 41fff7bf7ddb1d118898db1ddba43b95ba6ed0bb