From d03eaf90ee28df7f91c85d2ce05151e15d6f57e1 Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Tue, 27 Feb 2018 18:42:31 -0800 Subject: [PATCH 01/20] Update System.Memory --- .../Advanced/AdvancedImageExtensions.cs | 3 +- src/ImageSharp/Common/Extensions/SimdUtils.cs | 8 ++--- .../Formats/Jpeg/Common/Block8x8.cs | 5 +-- .../Formats/Jpeg/Common/Block8x8F.cs | 4 +-- .../JpegColorConverter.FromYCbCrSimd.cs | 9 ++--- .../JpegColorConverter.FromYCbCrSimdAvx2.cs | 9 ++--- .../ColorConverters/JpegColorConverter.cs | 2 +- .../Formats/Jpeg/Common/GenericBlock8x8.cs | 2 +- .../Formats/Png/Filters/AverageFilter.cs | 11 ++++--- .../Formats/Png/Filters/PaethFilter.cs | 11 ++++--- .../Formats/Png/Filters/SubFilter.cs | 7 ++-- .../Formats/Png/Filters/UpFilter.cs | 11 ++++--- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 4 +-- src/ImageSharp/ImageSharp.csproj | 6 ++-- src/ImageSharp/Memory/BufferExtensions.cs | 3 +- src/ImageSharp/Memory/SpanHelper.cs | 7 ++-- .../PixelOperations{TPixel}.Generated.cs | 33 ++++++++++--------- .../Rgba32.PixelOperations.Generated.cs | 26 +++++++-------- .../PixelFormats/PixelOperations{TPixel}.cs | 9 ++--- .../PixelFormats/Rgba32.PixelOperations.cs | 4 +-- .../Processors/Transforms/WeightsWindow.cs | 6 ++-- .../ImageSharp.Benchmarks.csproj | 4 +-- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 3 +- .../Memory/ArrayPoolMemoryManagerTests.cs | 2 +- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 4 +-- .../Memory/BufferTestSuite.cs | 7 ++-- .../Memory/SpanUtilityTests.cs | 20 +++++------ 27 files changed, 116 insertions(+), 104 deletions(-) diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index 612ced5d8d..24d2dd4cc4 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -148,6 +149,6 @@ namespace SixLabors.ImageSharp.Advanced /// A reference to the element. private static ref TPixel DangerousGetPinnableReferenceToPixelBuffer(IPixelSource source) where TPixel : struct, IPixel - => ref source.PixelBuffer.Span.DangerousGetPinnableReference(); + => ref MemoryMarshal.GetReference(source.PixelBuffer.Span); } } diff --git a/src/ImageSharp/Common/Extensions/SimdUtils.cs b/src/ImageSharp/Common/Extensions/SimdUtils.cs index 7f46b7a847..7b77fefcac 100644 --- a/src/ImageSharp/Common/Extensions/SimdUtils.cs +++ b/src/ImageSharp/Common/Extensions/SimdUtils.cs @@ -76,8 +76,8 @@ namespace SixLabors.ImageSharp return; } - ref Vector srcBase = ref Unsafe.As>(ref source.DangerousGetPinnableReference()); - ref Octet.OfByte destBase = ref Unsafe.As(ref dest.DangerousGetPinnableReference()); + ref Vector srcBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Octet.OfByte destBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); int n = source.Length / 8; Vector magick = new Vector(32768.0f); @@ -117,8 +117,8 @@ namespace SixLabors.ImageSharp return; } - ref Vector srcBase = ref Unsafe.As>(ref source.DangerousGetPinnableReference()); - ref Octet.OfByte destBase = ref Unsafe.As(ref dest.DangerousGetPinnableReference()); + ref Vector srcBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Octet.OfByte destBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); int n = source.Length / 8; Vector magick = new Vector(32768.0f); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs index 1066cfa808..11a456ef9b 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; namespace SixLabors.ImageSharp.Formats.Jpeg.Common @@ -34,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common public Block8x8(Span coefficients) { ref byte selfRef = ref Unsafe.As(ref this); - ref byte sourceRef = ref coefficients.NonPortableCast().DangerousGetPinnableReference(); + ref byte sourceRef = ref MemoryMarshal.GetReference(coefficients.NonPortableCast()); Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short)); } @@ -204,7 +205,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common public void CopyTo(Span destination) { ref byte selfRef = ref Unsafe.As(ref this); - ref byte destRef = ref destination.NonPortableCast().DangerousGetPinnableReference(); + ref byte destRef = ref MemoryMarshal.GetReference(destination.NonPortableCast()); Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short)); } diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs index 2dd42288cb..f45b5df4eb 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs @@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common [MethodImpl(MethodImplOptions.AggressiveInlining)] public void LoadFrom(Span source) { - ref byte s = ref Unsafe.As(ref source.DangerousGetPinnableReference()); + ref byte s = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref byte d = ref Unsafe.As(ref this); Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float)); @@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyTo(Span dest) { - ref byte d = ref Unsafe.As(ref dest.DangerousGetPinnableReference()); + ref byte d = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); ref byte s = ref Unsafe.As(ref this); Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float)); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs index a7fc136afe..2f214f88a9 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Tuples; namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters @@ -37,14 +38,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisable by 8!"); ref Vector4Pair yBase = - ref Unsafe.As(ref values.Component0.DangerousGetPinnableReference()); + ref Unsafe.As(ref MemoryMarshal.GetReference(values.Component0)); ref Vector4Pair cbBase = - ref Unsafe.As(ref values.Component1.DangerousGetPinnableReference()); + ref Unsafe.As(ref MemoryMarshal.GetReference(values.Component1)); ref Vector4Pair crBase = - ref Unsafe.As(ref values.Component2.DangerousGetPinnableReference()); + ref Unsafe.As(ref MemoryMarshal.GetReference(values.Component2)); ref Vector4Octet resultBase = - ref Unsafe.As(ref result.DangerousGetPinnableReference()); + ref Unsafe.As(ref MemoryMarshal.GetReference(result)); var chromaOffset = new Vector4(-128f); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs index 77e74c32b0..f8a4514221 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Tuples; // ReSharper disable ImpureMethodCallOnReadonlyValueField @@ -46,14 +47,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters } ref Vector yBase = - ref Unsafe.As>(ref values.Component0.DangerousGetPinnableReference()); + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); ref Vector cbBase = - ref Unsafe.As>(ref values.Component1.DangerousGetPinnableReference()); + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); ref Vector crBase = - ref Unsafe.As>(ref values.Component2.DangerousGetPinnableReference()); + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); ref Vector4Octet resultBase = - ref Unsafe.As(ref result.DangerousGetPinnableReference()); + ref Unsafe.As(ref MemoryMarshal.GetReference(result)); var chromaOffset = new Vector(-128f); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs index e0abc3215c..23c2071cc6 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters /// /// A stack-only struct to reference the input buffers using -s. /// - public struct ComponentValues + public ref struct ComponentValues { /// /// The component count diff --git a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs index e20e850d74..09a7eb73aa 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common ref byte blockStart = ref Unsafe.As, byte>(ref this); ref byte imageStart = ref Unsafe.As( - ref Unsafe.Add(ref source.GetRowSpan(sourceY).DangerousGetPinnableReference(), sourceX)); + ref Unsafe.Add(ref MemoryMarshal.GetReference(source.GetRowSpan(sourceY)), sourceX)); int blockRowSizeInBytes = 8 * Unsafe.SizeOf(); int imageRowSizeInBytes = source.Width * Unsafe.SizeOf(); diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index 0d3a65dbd8..de62d47029 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Png.Filters { @@ -24,8 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters { DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); // Average(x) + floor((Raw(x-bpp)+Prior(x))/2) for (int x = 1; x < scanline.Length; x++) @@ -60,9 +61,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); - ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); + ref byte resultBaseRef = ref MemoryMarshal.GetReference(result); sum = 0; // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index 08e4938804..7e05d736f9 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Png.Filters { @@ -25,8 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters { DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) int offset = bytesPerPixel + 1; @@ -61,9 +62,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); - ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); + ref byte resultBaseRef = ref MemoryMarshal.GetReference(result); sum = 0; // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp)) diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index 5ee8664400..c0db7da935 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Png.Filters { @@ -21,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(Span scanline, int bytesPerPixel) { - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); // Sub(x) + Raw(x-bpp) for (int x = 1; x < scanline.Length; x++) @@ -52,8 +53,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters { DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte resultBaseRef = ref MemoryMarshal.GetReference(result); sum = 0; // Sub(x) = Raw(x) - Raw(x-bpp) diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index 6e8f780e5c..81c063ea9e 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Png.Filters { @@ -23,8 +24,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters { DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); // Up(x) + Prior(x) for (int x = 1; x < scanline.Length; x++) @@ -48,9 +49,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); - ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); + ref byte resultBaseRef = ref MemoryMarshal.GetReference(result); sum = 0; // Up(x) = Raw(x) - Prior(x) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index fbff0ae1d9..c1dccdcafc 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -701,7 +701,7 @@ namespace SixLabors.ImageSharp.Formats.Png Span rowSpan = pixels.GetPixelRowSpan(this.currentRow); // Trim the first marker byte from the buffer - var scanlineBuffer = new Span(defilteredScanline, 1); + var scanlineBuffer = new Span(defilteredScanline, 1, defilteredScanline.Length - 1); switch (this.pngColorType) { @@ -932,7 +932,7 @@ namespace SixLabors.ImageSharp.Formats.Png var color = default(TPixel); // Trim the first marker byte from the buffer - var scanlineBuffer = new Span(defilteredScanline, 1); + var scanlineBuffer = new Span(defilteredScanline, 1, defilteredScanline.Length - 1); switch (this.pngColorType) { diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index cb0539f786..86a0ab7ea5 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -29,6 +29,7 @@ portable True IOperation + 7.2 @@ -40,12 +41,11 @@ All - - + + - diff --git a/src/ImageSharp/Memory/BufferExtensions.cs b/src/ImageSharp/Memory/BufferExtensions.cs index 919a6ef345..dd3114c21c 100644 --- a/src/ImageSharp/Memory/BufferExtensions.cs +++ b/src/ImageSharp/Memory/BufferExtensions.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Memory { @@ -53,7 +54,7 @@ namespace SixLabors.ImageSharp.Memory public static ref T DangerousGetPinnableReference(this IBuffer buffer) where T : struct => - ref buffer.Span.DangerousGetPinnableReference(); + ref MemoryMarshal.GetReference(buffer.Span); public static void Read(this Stream stream, IManagedByteBuffer buffer) { diff --git a/src/ImageSharp/Memory/SpanHelper.cs b/src/ImageSharp/Memory/SpanHelper.cs index 73bc5f55d8..0c327484a0 100644 --- a/src/ImageSharp/Memory/SpanHelper.cs +++ b/src/ImageSharp/Memory/SpanHelper.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Memory { @@ -22,7 +23,7 @@ namespace SixLabors.ImageSharp.Memory public static ref Vector FetchVector(this Span span) where T : struct { - return ref Unsafe.As>(ref span.DangerousGetPinnableReference()); + return ref Unsafe.As>(ref MemoryMarshal.GetReference(span)); } /// @@ -39,8 +40,8 @@ namespace SixLabors.ImageSharp.Memory DebugGuard.MustBeLessThanOrEqualTo(count, source.Length, nameof(count)); DebugGuard.MustBeLessThanOrEqualTo(count, destination.Length, nameof(count)); - ref byte srcRef = ref Unsafe.As(ref source.DangerousGetPinnableReference()); - ref byte destRef = ref Unsafe.As(ref destination.DangerousGetPinnableReference()); + ref byte srcRef = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref byte destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); int byteCount = Unsafe.SizeOf() * count; diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs index 9553ec82d6..964fa98f95 100644 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs @@ -6,6 +6,7 @@ namespace SixLabors.ImageSharp.PixelFormats { using System; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; public partial class PixelOperations { @@ -20,8 +21,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Rgba32 sourceRef = ref source.DangerousGetPinnableReference(); - ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(source); + ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); Rgba32 rgba = new Rgba32(0, 0, 0, 255); @@ -57,8 +58,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Rgba32 destBaseRef = ref dest.DangerousGetPinnableReference(); + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba32 destBaseRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -91,8 +92,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Bgra32 sourceRef = ref source.DangerousGetPinnableReference(); - ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(source); + ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); Rgba32 rgba = new Rgba32(0, 0, 0, 255); @@ -128,8 +129,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Bgra32 destBaseRef = ref dest.DangerousGetPinnableReference(); + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destBaseRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -162,8 +163,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Rgb24 sourceRef = ref source.DangerousGetPinnableReference(); - ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(source); + ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); Rgba32 rgba = new Rgba32(0, 0, 0, 255); @@ -199,8 +200,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Rgb24 destBaseRef = ref dest.DangerousGetPinnableReference(); + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destBaseRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -233,8 +234,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Bgr24 sourceRef = ref source.DangerousGetPinnableReference(); - ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(source); + ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); Rgba32 rgba = new Rgba32(0, 0, 0, 255); @@ -270,8 +271,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Bgr24 destBaseRef = ref dest.DangerousGetPinnableReference(); + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destBaseRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { diff --git a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs index 659e702281..43060539dc 100644 --- a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp { using System; using System.Runtime.CompilerServices; - + using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; /// @@ -22,8 +22,8 @@ namespace SixLabors.ImageSharp { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Rgb24 sourceRef = ref source.DangerousGetPinnableReference(); - ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference(); + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < count; i++) { @@ -38,8 +38,8 @@ namespace SixLabors.ImageSharp { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Rgb24 destRef = ref dest.DangerousGetPinnableReference(); + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -54,8 +54,8 @@ namespace SixLabors.ImageSharp { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Bgr24 sourceRef = ref source.DangerousGetPinnableReference(); - ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference(); + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < count; i++) { @@ -70,8 +70,8 @@ namespace SixLabors.ImageSharp { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Bgr24 destRef = ref dest.DangerousGetPinnableReference(); + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -86,8 +86,8 @@ namespace SixLabors.ImageSharp { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Bgra32 sourceRef = ref source.DangerousGetPinnableReference(); - ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference(); + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < count; i++) { @@ -102,8 +102,8 @@ namespace SixLabors.ImageSharp { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Bgra32 destRef = ref dest.DangerousGetPinnableReference(); + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 4f879fbdc7..6f79752406 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.PixelFormats { @@ -30,8 +31,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(sourceVectors, nameof(sourceVectors), destColors, nameof(destColors), count); - ref Vector4 sourceRef = ref sourceVectors.DangerousGetPinnableReference(); - ref TPixel destRef = ref destColors.DangerousGetPinnableReference(); + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors); + ref TPixel destRef = ref MemoryMarshal.GetReference(destColors); for (int i = 0; i < count; i++) { @@ -51,8 +52,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(sourceColors, nameof(sourceColors), destVectors, nameof(destVectors), count); - ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference(); - ref Vector4 destRef = ref destVectors.DangerousGetPinnableReference(); + ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors); + ref Vector4 destRef = ref MemoryMarshal.GetReference(destVectors); for (int i = 0; i < count; i++) { diff --git a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs index 552ac0a018..89a4aba264 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs @@ -57,8 +57,8 @@ namespace SixLabors.ImageSharp int unpackedRawCount = count * 4; - ref uint sourceBase = ref Unsafe.As(ref sourceColors.DangerousGetPinnableReference()); - ref UnpackedRGBA destBaseAsUnpacked = ref Unsafe.As(ref destVectors.DangerousGetPinnableReference()); + ref uint sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(sourceColors)); + ref UnpackedRGBA destBaseAsUnpacked = ref Unsafe.As(ref MemoryMarshal.GetReference(destVectors)); ref Vector destBaseAsUInt = ref Unsafe.As>(ref destBaseAsUnpacked); ref Vector destBaseAsFloat = ref Unsafe.As>(ref destBaseAsUnpacked); diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs index 399b3db842..26aaec502f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs @@ -4,7 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; - +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Processing.Processors @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { ref float horizontalValues = ref this.GetStartReference(); int left = this.Left; - ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); + ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), left + sourceX); // Destination color components Vector4 result = Vector4.Zero; @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { ref float horizontalValues = ref this.GetStartReference(); int left = this.Left; - ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); + ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), left + sourceX); // Destination color components Vector4 result = Vector4.Zero; diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 2e0b935155..021b9ead74 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -19,9 +19,7 @@ - - - + diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 16f062c6ef..d6ea4a130f 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -16,7 +16,8 @@ - + + diff --git a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs index a199bb319d..c73ce96313 100644 --- a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs +++ b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Tests.Memory public void ReleaseRetainedResources_ReplacesInnerArrayPool(bool keepBufferAlive) { IBuffer buffer = this.MemoryManager.Allocate(32); - ref int ptrToPrev0 = ref buffer.Span.DangerousGetPinnableReference(); + ref int ptrToPrev0 = ref MemoryMarshal.GetReference(buffer.Span); if (!keepBufferAlive) { diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 565e06572b..82163d2bb4 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -5,7 +5,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { using System; using System.Runtime.CompilerServices; - + using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Tests.Common; using SixLabors.Primitives; @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Memory public static void SpanPointsTo(Span span, IBuffer buffer, int bufferOffset = 0) where T : struct { - ref T actual = ref span.DangerousGetPinnableReference(); + ref T actual = ref MemoryMarshal.GetReference(span); ref T expected = ref Unsafe.Add(ref buffer.DangerousGetPinnableReference(), bufferOffset); Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position"); diff --git a/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs b/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs index 50477cb5cf..eff1f197a0 100644 --- a/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs +++ b/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using Xunit; // ReSharper disable InconsistentNaming @@ -165,9 +166,9 @@ namespace SixLabors.ImageSharp.Tests.Memory { using (IBuffer buffer = this.Allocate(desiredLength, false, testManagedByteBuffer)) { - ref T a = ref buffer.Span.DangerousGetPinnableReference(); - ref T b = ref buffer.Span.DangerousGetPinnableReference(); - ref T c = ref buffer.Span.DangerousGetPinnableReference(); + ref T a = ref MemoryMarshal.GetReference(buffer.Span); + ref T b = ref MemoryMarshal.GetReference(buffer.Span); + ref T c = ref MemoryMarshal.GetReference(buffer.Span); Assert.True(Unsafe.AreSame(ref a, ref b)); Assert.True(Unsafe.AreSame(ref b, ref c)); diff --git a/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs index 049c4c6ba9..23bc297436 100644 --- a/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs +++ b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs @@ -78,8 +78,8 @@ namespace SixLabors.ImageSharp.Tests.Memory TestStructs.Foo[] source = TestStructs.Foo.CreateArray(count + 2); TestStructs.Foo[] dest = new TestStructs.Foo[count + 5]; - var apSource = new Span(source, 1); - var apDest = new Span(dest, 1); + var apSource = new Span(source, 1, source.Length - 1); + var apDest = new Span(dest, 1, dest.Length - 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -101,8 +101,8 @@ namespace SixLabors.ImageSharp.Tests.Memory TestStructs.AlignedFoo[] source = TestStructs.AlignedFoo.CreateArray(count + 2); TestStructs.AlignedFoo[] dest = new TestStructs.AlignedFoo[count + 5]; - var apSource = new Span(source, 1); - var apDest = new Span(dest, 1); + var apSource = new Span(source, 1, source.Length - 1); + var apDest = new Span(dest, 1, dest.Length - 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -124,8 +124,8 @@ namespace SixLabors.ImageSharp.Tests.Memory int[] source = CreateTestInts(count + 2); int[] dest = new int[count + 5]; - var apSource = new Span(source, 1); - var apDest = new Span(dest, 1); + var apSource = new Span(source, 1, source.Length - 1); + var apDest = new Span(dest, 1, dest.Length - 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -148,8 +148,8 @@ namespace SixLabors.ImageSharp.Tests.Memory TestStructs.Foo[] source = TestStructs.Foo.CreateArray(count + 2); byte[] dest = new byte[destCount + sizeof(TestStructs.Foo) * 2]; - var apSource = new Span(source, 1); - var apDest = new Span(dest, sizeof(TestStructs.Foo)); + var apSource = new Span(source, 1, source.Length - 1); + var apDest = new Span(dest, sizeof(TestStructs.Foo), dest.Length - sizeof(TestStructs.Foo)); SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(TestStructs.Foo)); @@ -171,8 +171,8 @@ namespace SixLabors.ImageSharp.Tests.Memory TestStructs.AlignedFoo[] source = TestStructs.AlignedFoo.CreateArray(count + 2); byte[] dest = new byte[destCount + sizeof(TestStructs.AlignedFoo) * 2]; - var apSource = new Span(source, 1); - var apDest = new Span(dest, sizeof(TestStructs.AlignedFoo)); + var apSource = new Span(source, 1, source.Length - 1); + var apDest = new Span(dest, sizeof(TestStructs.AlignedFoo), dest.Length - sizeof(TestStructs.AlignedFoo)); SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(TestStructs.AlignedFoo)); From 330601fd0a86312bc650a8d1a34cd1fca9682027 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 28 Feb 2018 10:10:41 +0100 Subject: [PATCH 02/20] Refactored Image Formats management into its own class --- src/ImageSharp/Configuration.cs | 94 +++------ src/ImageSharp/Formats/ImageFormatsManager.cs | 185 ++++++++++++++++++ 2 files changed, 209 insertions(+), 70 deletions(-) create mode 100644 src/ImageSharp/Formats/ImageFormatsManager.cs diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 9a627eeb77..d53d9a9a59 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -24,28 +24,8 @@ namespace SixLabors.ImageSharp /// /// A lazily initialized configuration default instance. /// - private static readonly Lazy Lazy = new Lazy(CreateDefaultInstance); - - /// - /// The list of supported keyed to mime types. - /// - private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary(); - - /// - /// The list of supported keyed to mime types. - /// - private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); - - /// - /// The list of supported s. - /// - private readonly ConcurrentBag imageFormats = new ConcurrentBag(); - - /// - /// The list of supported s. - /// - private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); - + private static readonly Lazy Lazy = new Lazy(CreateDefaultInstance); + /// /// Initializes a new instance of the class. /// @@ -81,7 +61,12 @@ namespace SixLabors.ImageSharp /// /// Gets the currently registered s. /// - public IEnumerable ImageFormats => this.imageFormats; + public IEnumerable ImageFormats => this.FormatsManager.ImageFormats; + + /// + /// Gets or sets the that is currently in use. + /// + public ImageFormatsManager FormatsManager { get; set; } = new ImageFormatsManager(); /// /// Gets or sets the that is currently in use. @@ -91,22 +76,22 @@ namespace SixLabors.ImageSharp /// /// Gets the maximum header size of all the formats. /// - internal int MaxHeaderSize { get; private set; } - + internal int MaxHeaderSize => this.FormatsManager.MaxHeaderSize; + /// /// Gets the currently registered s. /// - internal IEnumerable FormatDetectors => this.imageFormatDetectors; + internal IEnumerable FormatDetectors => this.FormatsManager.FormatDetectors; /// /// Gets the currently registered s. /// - internal IEnumerable> ImageDecoders => this.mimeTypeDecoders; + internal IEnumerable> ImageDecoders => this.FormatsManager.ImageDecoders; /// /// Gets the currently registered s. /// - internal IEnumerable> ImageEncoders => this.mimeTypeEncoders; + internal IEnumerable> ImageEncoders => this.FormatsManager.ImageEncoders; #if !NETSTANDARD1_1 /// @@ -135,11 +120,8 @@ namespace SixLabors.ImageSharp /// /// The format to register as a known format. public void AddImageFormat(IImageFormat format) - { - Guard.NotNull(format, nameof(format)); - Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); - Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); - this.imageFormats.Add(format); + { + this.FormatsManager.AddImageFormat(format); } /// @@ -149,7 +131,7 @@ namespace SixLabors.ImageSharp /// The if found otherwise null public IImageFormat FindFormatByFileExtension(string extension) { - return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); + return this.FormatsManager.FindFormatByFileExtension(extension); } /// @@ -159,7 +141,7 @@ namespace SixLabors.ImageSharp /// The if found; otherwise null public IImageFormat FindFormatByMimeType(string mimeType) { - return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); + return this.FormatsManager.FindFormatByMimeType(mimeType); } /// @@ -169,10 +151,7 @@ namespace SixLabors.ImageSharp /// The encoder to use, public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) { - Guard.NotNull(imageFormat, nameof(imageFormat)); - Guard.NotNull(encoder, nameof(encoder)); - this.AddImageFormat(imageFormat); - this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); + this.FormatsManager.SetEncoder(imageFormat, encoder); } /// @@ -182,10 +161,7 @@ namespace SixLabors.ImageSharp /// The decoder to use, public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) { - Guard.NotNull(imageFormat, nameof(imageFormat)); - Guard.NotNull(decoder, nameof(decoder)); - this.AddImageFormat(imageFormat); - this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); + this.FormatsManager.SetDecoder(imageFormat, decoder); } /// @@ -193,7 +169,7 @@ namespace SixLabors.ImageSharp /// public void ClearImageFormatDetectors() { - this.imageFormatDetectors = new ConcurrentBag(); + this.FormatsManager.ClearImageFormatDetectors(); } /// @@ -202,9 +178,7 @@ namespace SixLabors.ImageSharp /// The detector to add public void AddImageFormatDetector(IImageFormatDetector detector) { - Guard.NotNull(detector, nameof(detector)); - this.imageFormatDetectors.Add(detector); - this.SetMaxHeaderSize(); + this.FormatsManager.AddImageFormatDetector(detector); } /// @@ -214,13 +188,7 @@ namespace SixLabors.ImageSharp /// The if found otherwise null public IImageDecoder FindDecoder(IImageFormat format) { - Guard.NotNull(format, nameof(format)); - if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)) - { - return decoder; - } - - return null; + return this.FormatsManager.FindDecoder(format); } /// @@ -230,13 +198,7 @@ namespace SixLabors.ImageSharp /// The if found otherwise null public IImageEncoder FindEncoder(IImageFormat format) { - Guard.NotNull(format, nameof(format)); - if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)) - { - return encoder; - } - - return null; + return this.FormatsManager.FindEncoder(format); } /// @@ -254,14 +216,6 @@ namespace SixLabors.ImageSharp new JpegConfigurationModule(), new GifConfigurationModule(), new BmpConfigurationModule()); - } - - /// - /// Sets the max header size. - /// - private void SetMaxHeaderSize() - { - this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); - } + } } } diff --git a/src/ImageSharp/Formats/ImageFormatsManager.cs b/src/ImageSharp/Formats/ImageFormatsManager.cs new file mode 100644 index 0000000000..b9307e4a05 --- /dev/null +++ b/src/ImageSharp/Formats/ImageFormatsManager.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Collection of Image Formats to be used in class. + /// + public class ImageFormatsManager + { + /// + /// Initializes a new instance of the class. + /// + public ImageFormatsManager() + { + } + + /// + /// The list of supported keyed to mime types. + /// + private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary(); + + /// + /// The list of supported keyed to mime types. + /// + private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); + + /// + /// The list of supported s. + /// + private readonly ConcurrentBag imageFormats = new ConcurrentBag(); + + /// + /// The list of supported s. + /// + private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); + + /// + /// Gets the maximum header size of all the formats. + /// + internal int MaxHeaderSize { get; private set; } + + /// + /// Gets the currently registered s. + /// + public IEnumerable ImageFormats => this.imageFormats; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable FormatDetectors => this.imageFormatDetectors; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageDecoders => this.mimeTypeDecoders; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageEncoders => this.mimeTypeEncoders; + + /// + /// Registers a new format provider. + /// + /// The format to register as a known format. + public void AddImageFormat(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); + Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); + this.imageFormats.Add(format); + } + + /// + /// For the specified file extensions type find the e . + /// + /// The extension to discover + /// The if found otherwise null + public IImageFormat FindFormatByFileExtension(string extension) + { + return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); + } + + /// + /// For the specified mime type find the . + /// + /// The mime-type to discover + /// The if found; otherwise null + public IImageFormat FindFormatByMimeType(string mimeType) + { + return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); + } + + /// + /// Sets a specific image encoder as the encoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The encoder to use, + public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) + { + Guard.NotNull(imageFormat, nameof(imageFormat)); + Guard.NotNull(encoder, nameof(encoder)); + this.AddImageFormat(imageFormat); + this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); + } + + /// + /// Sets a specific image decoder as the decoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The decoder to use, + public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) + { + Guard.NotNull(imageFormat, nameof(imageFormat)); + Guard.NotNull(decoder, nameof(decoder)); + this.AddImageFormat(imageFormat); + this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); + } + + /// + /// Removes all the registered image format detectors. + /// + public void ClearImageFormatDetectors() + { + this.imageFormatDetectors = new ConcurrentBag(); + } + + /// + /// Adds a new detector for detecting mime types. + /// + /// The detector to add + public void AddImageFormatDetector(IImageFormatDetector detector) + { + Guard.NotNull(detector, nameof(detector)); + this.imageFormatDetectors.Add(detector); + this.SetMaxHeaderSize(); + } + + /// + /// For the specified mime type find the decoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageDecoder FindDecoder(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)) + { + return decoder; + } + + return null; + } + + /// + /// For the specified mime type find the encoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageEncoder FindEncoder(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)) + { + return encoder; + } + + return null; + } + + /// + /// Sets the max header size. + /// + private void SetMaxHeaderSize() + { + this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); + } + + + } +} From fbf78a9030fd916cef550d7ae360d15203c7d8f7 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 28 Feb 2018 10:36:43 +0100 Subject: [PATCH 03/20] stylecop whitespaces --- src/ImageSharp/Configuration.cs | 426 +++++++++--------- src/ImageSharp/Formats/ImageFormatsManager.cs | 316 +++++++------ 2 files changed, 370 insertions(+), 372 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index d53d9a9a59..65633f2c8a 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -1,221 +1,221 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -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.IO; -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp -{ - /// - /// Provides initialization code which allows extending the library. - /// - public sealed class Configuration - { - /// - /// A lazily initialized configuration default instance. - /// +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +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.IO; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp +{ + /// + /// Provides initialization code which allows extending the library. + /// + public sealed class Configuration + { + /// + /// A lazily initialized configuration default instance. + /// private static readonly Lazy Lazy = new Lazy(CreateDefaultInstance); - /// - /// Initializes a new instance of the class. - /// - public Configuration() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A collection of configuration modules to register - public Configuration(params IConfigurationModule[] configurationModules) - { - if (configurationModules != null) - { - foreach (IConfigurationModule p in configurationModules) - { - p.Configure(this); - } - } - } - - /// - /// Gets the default instance. - /// - public static Configuration Default { get; } = Lazy.Value; - - /// - /// Gets the global parallel options for processing tasks in parallel. - /// - public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; - - /// - /// Gets the currently registered s. - /// + /// + /// Initializes a new instance of the class. + /// + public Configuration() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A collection of configuration modules to register + public Configuration(params IConfigurationModule[] configurationModules) + { + if (configurationModules != null) + { + foreach (IConfigurationModule p in configurationModules) + { + p.Configure(this); + } + } + } + + /// + /// Gets the default instance. + /// + public static Configuration Default { get; } = Lazy.Value; + + /// + /// Gets the global parallel options for processing tasks in parallel. + /// + public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; + + /// + /// Gets the currently registered s. + /// public IEnumerable ImageFormats => this.FormatsManager.ImageFormats; - /// - /// Gets or sets the that is currently in use. - /// - public ImageFormatsManager FormatsManager { get; set; } = new ImageFormatsManager(); - - /// - /// Gets or sets the that is currently in use. - /// - public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateDefault(); - - /// - /// Gets the maximum header size of all the formats. - /// + /// + /// Gets or sets the that is currently in use. + /// + public ImageFormatsManager FormatsManager { get; set; } = new ImageFormatsManager(); + + /// + /// Gets or sets the that is currently in use. + /// + public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateDefault(); + + /// + /// Gets the maximum header size of all the formats. + /// internal int MaxHeaderSize => this.FormatsManager.MaxHeaderSize; - /// - /// Gets the currently registered s. - /// - internal IEnumerable FormatDetectors => this.FormatsManager.FormatDetectors; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageDecoders => this.FormatsManager.ImageDecoders; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageEncoders => this.FormatsManager.ImageEncoders; - -#if !NETSTANDARD1_1 - /// - /// Gets or sets the filesystem helper for accessing the local file system. - /// - internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); -#endif - - /// - /// Gets or sets the image operations provider factory. - /// - internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); - - /// - /// Registers a new format provider. - /// - /// The configuration provider to call configure on. - public void Configure(IConfigurationModule configuration) - { - Guard.NotNull(configuration, nameof(configuration)); - configuration.Configure(this); - } - - /// - /// Registers a new format provider. - /// - /// The format to register as a known format. - public void AddImageFormat(IImageFormat format) + /// + /// Gets the currently registered s. + /// + internal IEnumerable FormatDetectors => this.FormatsManager.FormatDetectors; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageDecoders => this.FormatsManager.ImageDecoders; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageEncoders => this.FormatsManager.ImageEncoders; + +#if !NETSTANDARD1_1 + /// + /// Gets or sets the filesystem helper for accessing the local file system. + /// + internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); +#endif + + /// + /// Gets or sets the image operations provider factory. + /// + internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); + + /// + /// Registers a new format provider. + /// + /// The configuration provider to call configure on. + public void Configure(IConfigurationModule configuration) + { + Guard.NotNull(configuration, nameof(configuration)); + configuration.Configure(this); + } + + /// + /// Registers a new format provider. + /// + /// The format to register as a known format. + public void AddImageFormat(IImageFormat format) + { + this.FormatsManager.AddImageFormat(format); + } + + /// + /// For the specified file extensions type find the e . + /// + /// The extension to discover + /// The if found otherwise null + public IImageFormat FindFormatByFileExtension(string extension) + { + return this.FormatsManager.FindFormatByFileExtension(extension); + } + + /// + /// For the specified mime type find the . + /// + /// The mime-type to discover + /// The if found; otherwise null + public IImageFormat FindFormatByMimeType(string mimeType) + { + return this.FormatsManager.FindFormatByMimeType(mimeType); + } + + /// + /// Sets a specific image encoder as the encoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The encoder to use, + public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) + { + this.FormatsManager.SetEncoder(imageFormat, encoder); + } + + /// + /// Sets a specific image decoder as the decoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The decoder to use, + public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) + { + this.FormatsManager.SetDecoder(imageFormat, decoder); + } + + /// + /// Removes all the registered image format detectors. + /// + public void ClearImageFormatDetectors() + { + this.FormatsManager.ClearImageFormatDetectors(); + } + + /// + /// Adds a new detector for detecting mime types. + /// + /// The detector to add + public void AddImageFormatDetector(IImageFormatDetector detector) + { + this.FormatsManager.AddImageFormatDetector(detector); + } + + /// + /// For the specified mime type find the decoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageDecoder FindDecoder(IImageFormat format) + { + return this.FormatsManager.FindDecoder(format); + } + + /// + /// For the specified mime type find the encoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageEncoder FindEncoder(IImageFormat format) + { + return this.FormatsManager.FindEncoder(format); + } + + /// + /// Creates the default instance with the following s preregistered: + /// + /// + /// + /// + /// + /// The default configuration of + internal static Configuration CreateDefaultInstance() { - this.FormatsManager.AddImageFormat(format); - } - - /// - /// For the specified file extensions type find the e . - /// - /// The extension to discover - /// The if found otherwise null - public IImageFormat FindFormatByFileExtension(string extension) - { - return this.FormatsManager.FindFormatByFileExtension(extension); - } - - /// - /// For the specified mime type find the . - /// - /// The mime-type to discover - /// The if found; otherwise null - public IImageFormat FindFormatByMimeType(string mimeType) - { - return this.FormatsManager.FindFormatByMimeType(mimeType); - } - - /// - /// Sets a specific image encoder as the encoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The encoder to use, - public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) - { - this.FormatsManager.SetEncoder(imageFormat, encoder); - } - - /// - /// Sets a specific image decoder as the decoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The decoder to use, - public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) - { - this.FormatsManager.SetDecoder(imageFormat, decoder); - } - - /// - /// Removes all the registered image format detectors. - /// - public void ClearImageFormatDetectors() - { - this.FormatsManager.ClearImageFormatDetectors(); - } - - /// - /// Adds a new detector for detecting mime types. - /// - /// The detector to add - public void AddImageFormatDetector(IImageFormatDetector detector) - { - this.FormatsManager.AddImageFormatDetector(detector); - } - - /// - /// For the specified mime type find the decoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageDecoder FindDecoder(IImageFormat format) - { - return this.FormatsManager.FindDecoder(format); - } - - /// - /// For the specified mime type find the encoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageEncoder FindEncoder(IImageFormat format) - { - return this.FormatsManager.FindEncoder(format); - } - - /// - /// Creates the default instance with the following s preregistered: - /// - /// - /// - /// - /// - /// The default configuration of - internal static Configuration CreateDefaultInstance() - { - return new Configuration( - new PngConfigurationModule(), - new JpegConfigurationModule(), - new GifConfigurationModule(), - new BmpConfigurationModule()); + return new Configuration( + new PngConfigurationModule(), + new JpegConfigurationModule(), + new GifConfigurationModule(), + new BmpConfigurationModule()); } - } -} + } +} diff --git a/src/ImageSharp/Formats/ImageFormatsManager.cs b/src/ImageSharp/Formats/ImageFormatsManager.cs index b9307e4a05..3a78965612 100644 --- a/src/ImageSharp/Formats/ImageFormatsManager.cs +++ b/src/ImageSharp/Formats/ImageFormatsManager.cs @@ -6,180 +6,178 @@ using System.Text; namespace SixLabors.ImageSharp.Formats { - /// - /// Collection of Image Formats to be used in class. + /// + /// Collection of Image Formats to be used in class. /// public class ImageFormatsManager { - /// - /// Initializes a new instance of the class. - /// - public ImageFormatsManager() - { - } + /// + /// The list of supported keyed to mime types. + /// + private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary(); + + /// + /// The list of supported keyed to mime types. + /// + private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); + + /// + /// The list of supported s. + /// + private readonly ConcurrentBag imageFormats = new ConcurrentBag(); - /// - /// The list of supported keyed to mime types. - /// - private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary(); - - /// - /// The list of supported keyed to mime types. - /// - private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); - - /// - /// The list of supported s. - /// - private readonly ConcurrentBag imageFormats = new ConcurrentBag(); - - /// - /// The list of supported s. - /// + /// + /// The list of supported s. + /// private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); - /// - /// Gets the maximum header size of all the formats. - /// + /// + /// Initializes a new instance of the class. + /// + public ImageFormatsManager() + { + } + + /// + /// Gets the maximum header size of all the formats. + /// internal int MaxHeaderSize { get; private set; } - /// - /// Gets the currently registered s. - /// + /// + /// Gets the currently registered s. + /// public IEnumerable ImageFormats => this.imageFormats; - /// - /// Gets the currently registered s. - /// - internal IEnumerable FormatDetectors => this.imageFormatDetectors; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageDecoders => this.mimeTypeDecoders; - - /// - /// Gets the currently registered s. - /// + /// + /// Gets the currently registered s. + /// + internal IEnumerable FormatDetectors => this.imageFormatDetectors; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageDecoders => this.mimeTypeDecoders; + + /// + /// Gets the currently registered s. + /// internal IEnumerable> ImageEncoders => this.mimeTypeEncoders; - /// - /// Registers a new format provider. - /// - /// The format to register as a known format. - public void AddImageFormat(IImageFormat format) - { - Guard.NotNull(format, nameof(format)); - Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); - Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); - this.imageFormats.Add(format); - } - - /// - /// For the specified file extensions type find the e . - /// - /// The extension to discover - /// The if found otherwise null - public IImageFormat FindFormatByFileExtension(string extension) - { - return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); - } - - /// - /// For the specified mime type find the . - /// - /// The mime-type to discover - /// The if found; otherwise null - public IImageFormat FindFormatByMimeType(string mimeType) - { - return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); - } - - /// - /// Sets a specific image encoder as the encoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The encoder to use, - public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) - { - Guard.NotNull(imageFormat, nameof(imageFormat)); - Guard.NotNull(encoder, nameof(encoder)); - this.AddImageFormat(imageFormat); - this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); - } - - /// - /// Sets a specific image decoder as the decoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The decoder to use, - public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) - { - Guard.NotNull(imageFormat, nameof(imageFormat)); - Guard.NotNull(decoder, nameof(decoder)); - this.AddImageFormat(imageFormat); - this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); - } - - /// - /// Removes all the registered image format detectors. - /// - public void ClearImageFormatDetectors() - { - this.imageFormatDetectors = new ConcurrentBag(); - } - - /// - /// Adds a new detector for detecting mime types. - /// - /// The detector to add - public void AddImageFormatDetector(IImageFormatDetector detector) - { - Guard.NotNull(detector, nameof(detector)); - this.imageFormatDetectors.Add(detector); - this.SetMaxHeaderSize(); - } - - /// - /// For the specified mime type find the decoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageDecoder FindDecoder(IImageFormat format) - { - Guard.NotNull(format, nameof(format)); - if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)) - { - return decoder; - } - - return null; - } - - /// - /// For the specified mime type find the encoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageEncoder FindEncoder(IImageFormat format) - { - Guard.NotNull(format, nameof(format)); - if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)) - { - return encoder; - } - - return null; + /// + /// Registers a new format provider. + /// + /// The format to register as a known format. + public void AddImageFormat(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); + Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); + this.imageFormats.Add(format); + } + + /// + /// For the specified file extensions type find the e . + /// + /// The extension to discover + /// The if found otherwise null + public IImageFormat FindFormatByFileExtension(string extension) + { + return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); + } + + /// + /// For the specified mime type find the . + /// + /// The mime-type to discover + /// The if found; otherwise null + public IImageFormat FindFormatByMimeType(string mimeType) + { + return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); + } + + /// + /// Sets a specific image encoder as the encoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The encoder to use, + public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) + { + Guard.NotNull(imageFormat, nameof(imageFormat)); + Guard.NotNull(encoder, nameof(encoder)); + this.AddImageFormat(imageFormat); + this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); + } + + /// + /// Sets a specific image decoder as the decoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The decoder to use, + public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) + { + Guard.NotNull(imageFormat, nameof(imageFormat)); + Guard.NotNull(decoder, nameof(decoder)); + this.AddImageFormat(imageFormat); + this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); + } + + /// + /// Removes all the registered image format detectors. + /// + public void ClearImageFormatDetectors() + { + this.imageFormatDetectors = new ConcurrentBag(); + } + + /// + /// Adds a new detector for detecting mime types. + /// + /// The detector to add + public void AddImageFormatDetector(IImageFormatDetector detector) + { + Guard.NotNull(detector, nameof(detector)); + this.imageFormatDetectors.Add(detector); + this.SetMaxHeaderSize(); + } + + /// + /// For the specified mime type find the decoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageDecoder FindDecoder(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)) + { + return decoder; + } + + return null; } - /// - /// Sets the max header size. - /// - private void SetMaxHeaderSize() - { - this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); + /// + /// For the specified mime type find the encoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageEncoder FindEncoder(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)) + { + return encoder; + } + + return null; } - + /// + /// Sets the max header size. + /// + private void SetMaxHeaderSize() + { + this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); + } } } From efb6a8e06062996db78ff08bad38248d99f815ff Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 28 Feb 2018 11:49:27 +0100 Subject: [PATCH 04/20] Coverage test --- src/ImageSharp/Formats/ImageFormatsManager.cs | 5 +- .../Formats/ImageFormatsManagerTests.cs | 117 ++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs diff --git a/src/ImageSharp/Formats/ImageFormatsManager.cs b/src/ImageSharp/Formats/ImageFormatsManager.cs index 3a78965612..350c28b0a3 100644 --- a/src/ImageSharp/Formats/ImageFormatsManager.cs +++ b/src/ImageSharp/Formats/ImageFormatsManager.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs new file mode 100644 index 0000000000..3aeeff9379 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs @@ -0,0 +1,117 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using Moq; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public class ImageFormatsManagerTests + { + public ImageFormatsManager FormatsManagerEmpty { get; private set; } + public ImageFormatsManager DefaultFormatsManager { get; private set; } + + public ImageFormatsManagerTests() + { + this.DefaultFormatsManager = Configuration.Default.FormatsManager; + this.FormatsManagerEmpty = new ImageFormatsManager(); + } + + [Fact] + public void IfAutoloadWellknownFormatsIsTrueAllFormatsAreLoaded() + { + Assert.Equal(4, this.DefaultFormatsManager.ImageEncoders.Count()); + Assert.Equal(4, this.DefaultFormatsManager.ImageDecoders.Count()); + } + + [Fact] + public void AddImageFormatDetectorNullthrows() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.AddImageFormatDetector(null); + }); + } + + [Fact] + public void RegisterNullMimeTypeEncoder() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(null, new Mock().Object); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(ImageFormats.Bmp, null); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(null, null); + }); + } + + [Fact] + public void RegisterNullSetDecoder() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(null, new Mock().Object); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(ImageFormats.Bmp, null); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(null, null); + }); + } + + [Fact] + public void RegisterMimeTypeEncoderReplacesLast() + { + IImageEncoder encoder1 = new Mock().Object; + this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); + IImageEncoder found = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); + Assert.Equal(encoder1, found); + + IImageEncoder encoder2 = new Mock().Object; + this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); + IImageEncoder found2 = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); + Assert.Equal(encoder2, found2); + Assert.NotEqual(found, found2); + } + + [Fact] + public void RegisterMimeTypeDecoderReplacesLast() + { + IImageDecoder decoder1 = new Mock().Object; + this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); + IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); + Assert.Equal(decoder1, found); + + IImageDecoder decoder2 = new Mock().Object; + this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); + IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); + Assert.Equal(decoder2, found2); + Assert.NotEqual(found, found2); + } + + [Fact] + public void AddFormatCallsConfig() + { + var provider = new Mock(); + var config = new Configuration(); + config.Configure(provider.Object); + + provider.Verify(x => x.Configure(config)); + } + } +} From fbc9cf7d86cbae0b2273c902c34c0d5962e87e35 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 28 Feb 2018 12:50:55 +0100 Subject: [PATCH 05/20] renaming ImageFormatsManager to ImageFormatManager --- src/ImageSharp/Configuration.cs | 32 +++++++++---------- ...ormatsManager.cs => ImageFormatManager.cs} | 6 ++-- ...gerTests.cs => ImageFormatManagerTests.cs} | 14 ++++---- 3 files changed, 26 insertions(+), 26 deletions(-) rename src/ImageSharp/Formats/{ImageFormatsManager.cs => ImageFormatManager.cs} (96%) rename tests/ImageSharp.Tests/Formats/{ImageFormatsManagerTests.cs => ImageFormatManagerTests.cs} (89%) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 65633f2c8a..0f69194f2d 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -61,12 +61,12 @@ namespace SixLabors.ImageSharp /// /// Gets the currently registered s. /// - public IEnumerable ImageFormats => this.FormatsManager.ImageFormats; + public IEnumerable ImageFormats => this.ImageFormatsManager.ImageFormats; /// - /// Gets or sets the that is currently in use. + /// Gets or sets the that is currently in use. /// - public ImageFormatsManager FormatsManager { get; set; } = new ImageFormatsManager(); + public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager(); /// /// Gets or sets the that is currently in use. @@ -76,22 +76,22 @@ namespace SixLabors.ImageSharp /// /// Gets the maximum header size of all the formats. /// - internal int MaxHeaderSize => this.FormatsManager.MaxHeaderSize; + internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize; /// /// Gets the currently registered s. /// - internal IEnumerable FormatDetectors => this.FormatsManager.FormatDetectors; + internal IEnumerable FormatDetectors => this.ImageFormatsManager.FormatDetectors; /// /// Gets the currently registered s. /// - internal IEnumerable> ImageDecoders => this.FormatsManager.ImageDecoders; + internal IEnumerable> ImageDecoders => this.ImageFormatsManager.ImageDecoders; /// /// Gets the currently registered s. /// - internal IEnumerable> ImageEncoders => this.FormatsManager.ImageEncoders; + internal IEnumerable> ImageEncoders => this.ImageFormatsManager.ImageEncoders; #if !NETSTANDARD1_1 /// @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp /// The format to register as a known format. public void AddImageFormat(IImageFormat format) { - this.FormatsManager.AddImageFormat(format); + this.ImageFormatsManager.AddImageFormat(format); } /// @@ -131,7 +131,7 @@ namespace SixLabors.ImageSharp /// The if found otherwise null public IImageFormat FindFormatByFileExtension(string extension) { - return this.FormatsManager.FindFormatByFileExtension(extension); + return this.ImageFormatsManager.FindFormatByFileExtension(extension); } /// @@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp /// The if found; otherwise null public IImageFormat FindFormatByMimeType(string mimeType) { - return this.FormatsManager.FindFormatByMimeType(mimeType); + return this.ImageFormatsManager.FindFormatByMimeType(mimeType); } /// @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp /// The encoder to use, public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) { - this.FormatsManager.SetEncoder(imageFormat, encoder); + this.ImageFormatsManager.SetEncoder(imageFormat, encoder); } /// @@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp /// The decoder to use, public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) { - this.FormatsManager.SetDecoder(imageFormat, decoder); + this.ImageFormatsManager.SetDecoder(imageFormat, decoder); } /// @@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp /// public void ClearImageFormatDetectors() { - this.FormatsManager.ClearImageFormatDetectors(); + this.ImageFormatsManager.ClearImageFormatDetectors(); } /// @@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp /// The detector to add public void AddImageFormatDetector(IImageFormatDetector detector) { - this.FormatsManager.AddImageFormatDetector(detector); + this.ImageFormatsManager.AddImageFormatDetector(detector); } /// @@ -188,7 +188,7 @@ namespace SixLabors.ImageSharp /// The if found otherwise null public IImageDecoder FindDecoder(IImageFormat format) { - return this.FormatsManager.FindDecoder(format); + return this.ImageFormatsManager.FindDecoder(format); } /// @@ -198,7 +198,7 @@ namespace SixLabors.ImageSharp /// The if found otherwise null public IImageEncoder FindEncoder(IImageFormat format) { - return this.FormatsManager.FindEncoder(format); + return this.ImageFormatsManager.FindEncoder(format); } /// diff --git a/src/ImageSharp/Formats/ImageFormatsManager.cs b/src/ImageSharp/Formats/ImageFormatManager.cs similarity index 96% rename from src/ImageSharp/Formats/ImageFormatsManager.cs rename to src/ImageSharp/Formats/ImageFormatManager.cs index 350c28b0a3..67ba111474 100644 --- a/src/ImageSharp/Formats/ImageFormatsManager.cs +++ b/src/ImageSharp/Formats/ImageFormatManager.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats /// /// Collection of Image Formats to be used in class. /// - public class ImageFormatsManager + public class ImageFormatManager { /// /// The list of supported keyed to mime types. @@ -35,9 +35,9 @@ namespace SixLabors.ImageSharp.Formats private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public ImageFormatsManager() + public ImageFormatManager() { } diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs similarity index 89% rename from tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs rename to tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs index 3aeeff9379..bfaf26c6ac 100644 --- a/tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs +++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs @@ -13,19 +13,19 @@ using Xunit; namespace SixLabors.ImageSharp.Tests { - public class ImageFormatsManagerTests + public class ImageFormatManagerTests { - public ImageFormatsManager FormatsManagerEmpty { get; private set; } - public ImageFormatsManager DefaultFormatsManager { get; private set; } + public ImageFormatManager FormatsManagerEmpty { get; private set; } + public ImageFormatManager DefaultFormatsManager { get; private set; } - public ImageFormatsManagerTests() + public ImageFormatManagerTests() { - this.DefaultFormatsManager = Configuration.Default.FormatsManager; - this.FormatsManagerEmpty = new ImageFormatsManager(); + this.DefaultFormatsManager = Configuration.Default.ImageFormatsManager; + this.FormatsManagerEmpty = new ImageFormatManager(); } [Fact] - public void IfAutoloadWellknownFormatsIsTrueAllFormatsAreLoaded() + public void IfAutoloadWellKnownFormatsIsTrueAllFormatsAreLoaded() { Assert.Equal(4, this.DefaultFormatsManager.ImageEncoders.Count()); Assert.Equal(4, this.DefaultFormatsManager.ImageDecoders.Count()); From c293767e78e567bad50f9f586d347a0a061155a2 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 28 Feb 2018 13:52:03 +0100 Subject: [PATCH 06/20] changed test to ensure proper encoders/decoders are set --- .../Formats/ImageFormatManagerTests.cs | 222 +++++++++--------- 1 file changed, 117 insertions(+), 105 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs index bfaf26c6ac..a6f6600f05 100644 --- a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs +++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs @@ -1,117 +1,129 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.PixelFormats; -using Moq; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Gif; +using Moq; using Xunit; + namespace SixLabors.ImageSharp.Tests { public class ImageFormatManagerTests { - public ImageFormatManager FormatsManagerEmpty { get; private set; } - public ImageFormatManager DefaultFormatsManager { get; private set; } - - public ImageFormatManagerTests() - { - this.DefaultFormatsManager = Configuration.Default.ImageFormatsManager; - this.FormatsManagerEmpty = new ImageFormatManager(); - } - - [Fact] - public void IfAutoloadWellKnownFormatsIsTrueAllFormatsAreLoaded() - { - Assert.Equal(4, this.DefaultFormatsManager.ImageEncoders.Count()); - Assert.Equal(4, this.DefaultFormatsManager.ImageDecoders.Count()); + public ImageFormatManager FormatsManagerEmpty { get; private set; } + public ImageFormatManager DefaultFormatsManager { get; private set; } + + public ImageFormatManagerTests() + { + this.DefaultFormatsManager = Configuration.Default.ImageFormatsManager; + this.FormatsManagerEmpty = new ImageFormatManager(); + } + + [Fact] + public void IfAutoloadWellKnownFormatsIsTrueAllFormatsAreLoaded() + { + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + } + + [Fact] + public void AddImageFormatDetectorNullthrows() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.AddImageFormatDetector(null); + }); } - [Fact] - public void AddImageFormatDetectorNullthrows() - { - Assert.Throws(() => - { - this.DefaultFormatsManager.AddImageFormatDetector(null); - }); - } - - [Fact] - public void RegisterNullMimeTypeEncoder() - { - Assert.Throws(() => + [Fact] + public void RegisterNullMimeTypeEncoder() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(null, new Mock().Object); + }); + Assert.Throws(() => { - this.DefaultFormatsManager.SetEncoder(null, new Mock().Object); - }); - Assert.Throws(() => - { - this.DefaultFormatsManager.SetEncoder(ImageFormats.Bmp, null); - }); - Assert.Throws(() => - { - this.DefaultFormatsManager.SetEncoder(null, null); - }); - } - - [Fact] - public void RegisterNullSetDecoder() - { - Assert.Throws(() => - { - this.DefaultFormatsManager.SetDecoder(null, new Mock().Object); - }); - Assert.Throws(() => - { - this.DefaultFormatsManager.SetDecoder(ImageFormats.Bmp, null); - }); - Assert.Throws(() => - { - this.DefaultFormatsManager.SetDecoder(null, null); - }); - } - - [Fact] - public void RegisterMimeTypeEncoderReplacesLast() - { - IImageEncoder encoder1 = new Mock().Object; - this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); - IImageEncoder found = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); - Assert.Equal(encoder1, found); - - IImageEncoder encoder2 = new Mock().Object; - this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); - IImageEncoder found2 = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); - Assert.Equal(encoder2, found2); - Assert.NotEqual(found, found2); - } - - [Fact] - public void RegisterMimeTypeDecoderReplacesLast() - { - IImageDecoder decoder1 = new Mock().Object; - this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); - IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); - Assert.Equal(decoder1, found); - - IImageDecoder decoder2 = new Mock().Object; - this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); - IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); - Assert.Equal(decoder2, found2); - Assert.NotEqual(found, found2); - } - - [Fact] - public void AddFormatCallsConfig() - { - var provider = new Mock(); - var config = new Configuration(); - config.Configure(provider.Object); - - provider.Verify(x => x.Configure(config)); + this.DefaultFormatsManager.SetEncoder(ImageFormats.Bmp, null); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(null, null); + }); + } + + [Fact] + public void RegisterNullSetDecoder() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(null, new Mock().Object); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(ImageFormats.Bmp, null); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(null, null); + }); + } + + [Fact] + public void RegisterMimeTypeEncoderReplacesLast() + { + IImageEncoder encoder1 = new Mock().Object; + this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); + IImageEncoder found = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); + Assert.Equal(encoder1, found); + + IImageEncoder encoder2 = new Mock().Object; + this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); + IImageEncoder found2 = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); + Assert.Equal(encoder2, found2); + Assert.NotEqual(found, found2); + } + + [Fact] + public void RegisterMimeTypeDecoderReplacesLast() + { + IImageDecoder decoder1 = new Mock().Object; + this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); + IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); + Assert.Equal(decoder1, found); + + IImageDecoder decoder2 = new Mock().Object; + this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); + IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); + Assert.Equal(decoder2, found2); + Assert.NotEqual(found, found2); + } + + [Fact] + public void AddFormatCallsConfig() + { + var provider = new Mock(); + var config = new Configuration(); + config.Configure(provider.Object); + + provider.Verify(x => x.Configure(config)); } } } From 09dd14ac71881804b30219d476f5a03a5f0f7dcc Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Wed, 28 Feb 2018 10:05:04 -0800 Subject: [PATCH 07/20] Fix breaking changes from System.Memory in benchmarks --- tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs | 6 +++--- tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs index 7d8519875b..7bac44a982 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs @@ -3,7 +3,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { using System.Numerics; using System.Runtime.CompilerServices; - + using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Memory; @@ -37,8 +37,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [Benchmark(Baseline = true)] public void PerElement() { - ref Vector4 s = ref this.source.Span.DangerousGetPinnableReference(); - ref TPixel d = ref this.destination.Span.DangerousGetPinnableReference(); + ref Vector4 s = ref MemoryMarshal.GetReference(this.source.Span); + ref TPixel d = ref MemoryMarshal.GetReference(this.destination.Span); for (int i = 0; i < this.Count; i++) { diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 021b9ead74..6dcfbaf818 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -17,7 +17,9 @@ - + + + From 79916d4a2d6d8dc43b3bd79215714f943458e522 Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Wed, 28 Feb 2018 10:17:23 -0800 Subject: [PATCH 08/20] Annotate readonly structs --- src/ImageSharp/ColorSpaces/CieLab.cs | 2 +- src/ImageSharp/ColorSpaces/CieLch.cs | 2 +- src/ImageSharp/ColorSpaces/CieLchuv.cs | 2 +- src/ImageSharp/ColorSpaces/CieLuv.cs | 2 +- src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs | 2 +- src/ImageSharp/ColorSpaces/CieXyy.cs | 2 +- src/ImageSharp/ColorSpaces/CieXyz.cs | 2 +- src/ImageSharp/ColorSpaces/Cmyk.cs | 2 +- .../Conversion/Implementation/Rgb/RgbWorkingSpace.cs | 2 +- src/ImageSharp/ColorSpaces/Hsl.cs | 2 +- src/ImageSharp/ColorSpaces/Hsv.cs | 2 +- src/ImageSharp/ColorSpaces/HunterLab.cs | 2 +- src/ImageSharp/ColorSpaces/LinearRgb.cs | 2 +- src/ImageSharp/ColorSpaces/Lms.cs | 2 +- src/ImageSharp/ColorSpaces/Rgb.cs | 2 +- src/ImageSharp/ColorSpaces/YCbCr.cs | 2 +- src/ImageSharp/Formats/Gif/PackedField.cs | 2 +- src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs | 2 +- src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs | 2 +- .../Jpeg/GolangPort/Components/Decoder/InputProcessor.cs | 2 -- .../Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs | 2 +- .../Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs | 2 +- .../Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs | 2 +- src/ImageSharp/Memory/BufferArea{T}.cs | 2 +- .../MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs | 2 +- src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs | 2 +- .../MetaData/Profiles/ICC/Various/IccPositionNumber.cs | 2 +- src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs | 2 +- .../MetaData/Profiles/ICC/Various/IccResponseNumber.cs | 2 +- .../MetaData/Profiles/ICC/Various/IccScreeningChannel.cs | 2 +- .../MetaData/Profiles/ICC/Various/IccTagTableEntry.cs | 2 +- src/ImageSharp/Numerics/ValueSize.cs | 2 +- src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs | 2 +- 33 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/ImageSharp/ColorSpaces/CieLab.cs b/src/ImageSharp/ColorSpaces/CieLab.cs index 107be4cb2b..cb08d08bf9 100644 --- a/src/ImageSharp/ColorSpaces/CieLab.cs +++ b/src/ImageSharp/ColorSpaces/CieLab.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents a CIE L*a*b* 1976 color. /// /// - internal struct CieLab : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct CieLab : IColorVector, IEquatable, IAlmostEquatable { /// /// D50 standard illuminant. diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs index 834ef56a89..94443fd863 100644 --- a/src/ImageSharp/ColorSpaces/CieLch.cs +++ b/src/ImageSharp/ColorSpaces/CieLch.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color. /// /// - internal struct CieLch : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct CieLch : IColorVector, IEquatable, IAlmostEquatable { /// /// D50 standard illuminant. diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs index f35914d641..705b770d35 100644 --- a/src/ImageSharp/ColorSpaces/CieLchuv.cs +++ b/src/ImageSharp/ColorSpaces/CieLchuv.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. /// /// - internal struct CieLchuv : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct CieLchuv : IColorVector, IEquatable, IAlmostEquatable { /// /// D50 standard illuminant. diff --git a/src/ImageSharp/ColorSpaces/CieLuv.cs b/src/ImageSharp/ColorSpaces/CieLuv.cs index 9b52517083..b0ae048ab7 100644 --- a/src/ImageSharp/ColorSpaces/CieLuv.cs +++ b/src/ImageSharp/ColorSpaces/CieLuv.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// attempted perceptual uniformity /// /// - internal struct CieLuv : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct CieLuv : IColorVector, IEquatable, IAlmostEquatable { /// /// D65 standard illuminant. diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs index 487f464d8e..d0a70dd191 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents the coordinates of CIEXY chromaticity space /// - internal struct CieXyChromaticityCoordinates : IEquatable, IAlmostEquatable + internal readonly struct CieXyChromaticityCoordinates : IEquatable, IAlmostEquatable { /// /// Represents a that has X, Y values set to zero. diff --git a/src/ImageSharp/ColorSpaces/CieXyy.cs b/src/ImageSharp/ColorSpaces/CieXyy.cs index d5ef4b15d3..751830a0ba 100644 --- a/src/ImageSharp/ColorSpaces/CieXyy.cs +++ b/src/ImageSharp/ColorSpaces/CieXyy.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents an CIE xyY 1931 color /// /// - internal struct CieXyy : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct CieXyy : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has X, Y, and Y values set to zero. diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs index 908408000a..0f1866009b 100644 --- a/src/ImageSharp/ColorSpaces/CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/CieXyz.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents an CIE XYZ 1931 color /// /// - internal struct CieXyz : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct CieXyz : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has X, Y, and Z values set to zero. diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs index 2a58a5762a..2eb148a8c3 100644 --- a/src/ImageSharp/ColorSpaces/Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Cmyk.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents an CMYK (cyan, magenta, yellow, keyline) color. /// - internal struct Cmyk : IEquatable, IAlmostEquatable + internal readonly struct Cmyk : IEquatable, IAlmostEquatable { /// /// Represents a that has C, M, Y, and K values set to zero. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs index 8a2c66a80c..5a5c39647f 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// /// Trivial implementation of /// - internal struct RgbWorkingSpace : IRgbWorkingSpace + internal readonly struct RgbWorkingSpace : IRgbWorkingSpace { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/ColorSpaces/Hsl.cs b/src/ImageSharp/ColorSpaces/Hsl.cs index cf880f1548..1944ac0c6b 100644 --- a/src/ImageSharp/ColorSpaces/Hsl.cs +++ b/src/ImageSharp/ColorSpaces/Hsl.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents a Hsl (hue, saturation, lightness) color. /// - internal struct Hsl : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct Hsl : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has H, S, and L values set to zero. diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs index 9f47393792..45ffd7f121 100644 --- a/src/ImageSharp/ColorSpaces/Hsv.cs +++ b/src/ImageSharp/ColorSpaces/Hsv.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). /// - internal struct Hsv : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct Hsv : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has H, S, and V values set to zero. diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs index b5ba7c86c7..de42518d76 100644 --- a/src/ImageSharp/ColorSpaces/HunterLab.cs +++ b/src/ImageSharp/ColorSpaces/HunterLab.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents an Hunter LAB color. /// /// - internal struct HunterLab : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct HunterLab : IColorVector, IEquatable, IAlmostEquatable { /// /// D50 standard illuminant. diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs index 07889c3529..b8c446285a 100644 --- a/src/ImageSharp/ColorSpaces/LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/LinearRgb.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents an linear Rgb color with specified working space /// - internal struct LinearRgb : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct LinearRgb : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has R, G, and B values set to zero. diff --git a/src/ImageSharp/ColorSpaces/Lms.cs b/src/ImageSharp/ColorSpaces/Lms.cs index 82c291de3d..72ac16f213 100644 --- a/src/ImageSharp/ColorSpaces/Lms.cs +++ b/src/ImageSharp/ColorSpaces/Lms.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// named after their responsivity (sensitivity) at long, medium and short wavelengths. /// /// - internal struct Lms : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct Lms : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has L, M, and S values set to zero. diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs index 8ac8411b20..53fa6086df 100644 --- a/src/ImageSharp/ColorSpaces/Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Rgb.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents an RGB color with specified working space /// - internal struct Rgb : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct Rgb : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has R, G, and B values set to zero. diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs index 708a74308a..44a0b245d5 100644 --- a/src/ImageSharp/ColorSpaces/YCbCr.cs +++ b/src/ImageSharp/ColorSpaces/YCbCr.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// /// - internal struct YCbCr : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct YCbCr : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has Y, Cb, and Cr values set to zero. diff --git a/src/ImageSharp/Formats/Gif/PackedField.cs b/src/ImageSharp/Formats/Gif/PackedField.cs index 962e2082bf..28a415e2b8 100644 --- a/src/ImageSharp/Formats/Gif/PackedField.cs +++ b/src/ImageSharp/Formats/Gif/PackedField.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Represents a byte of data in a GIF data stream which contains a number /// of data items. /// - internal struct PackedField : IEquatable + internal readonly struct PackedField : IEquatable { /// /// The individual bits representing the packed byte. diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs index 0ec2297d76..d55e36bd48 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// Provides information about the Adobe marker segment. /// /// See the included 5116.DCT.pdf file in the source for more information. - internal struct AdobeMarker : IEquatable + internal readonly struct AdobeMarker : IEquatable { /// /// Gets the length of an adobe marker segment. diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs index cba7be5539..c856fd04a6 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// Provides information about the JFIF marker segment /// TODO: Thumbnail? /// - internal struct JFifMarker : IEquatable + internal readonly struct JFifMarker : IEquatable { /// /// Gets the length of an JFIF marker segment. diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index 88599808fc..fd6a7833a7 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -5,8 +5,6 @@ using System; using System.IO; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Memory; - namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { /// diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs index 2fb01c5c8c..7756a7e3ba 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder /// codeword size in bits and the 24 least significant bits hold the codeword. /// The maximum codeword size is 16 bits. /// - internal struct HuffmanLut + internal readonly struct HuffmanLut { /// /// The compiled representations of theHuffmanSpec. diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs index 8e40cb3689..1c8228aaa2 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder /// /// The Huffman encoding specifications. /// - internal struct HuffmanSpec + internal readonly struct HuffmanSpec { #pragma warning disable SA1118 // ParameterMustNotSpanMultipleLines diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs index ddc577270b..203a7b1eb2 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// Provides 8-bit lookup tables for converting from YCbCr to Rgb colorspace. /// Methods to build the tables are based on libjpeg implementation. /// - internal struct PdfJsYCbCrToRgbTables + internal readonly struct PdfJsYCbCrToRgbTables { /// /// The red red-chrominance table diff --git a/src/ImageSharp/Memory/BufferArea{T}.cs b/src/ImageSharp/Memory/BufferArea{T}.cs index 588eae483d..990b494fc7 100644 --- a/src/ImageSharp/Memory/BufferArea{T}.cs +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Memory /// This type is kind-of 2D Span, but it can live on heap. /// /// The element type - internal struct BufferArea + internal readonly struct BufferArea where T : struct { /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs index 17127110d6..c5b005ea09 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// Entry of ICC colorant table /// - internal struct IccColorantTableEntry : IEquatable + internal readonly struct IccColorantTableEntry : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs index 98107e8281..22916c1344 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// A specific color with a name /// - internal struct IccNamedColor : IEquatable + internal readonly struct IccNamedColor : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs index 6258ca2f36..d886dc099c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// Position of an object within an ICC profile /// - internal struct IccPositionNumber : IEquatable + internal readonly struct IccPositionNumber : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs index 1f96540df3..4070f835d6 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// ICC Profile ID /// - public struct IccProfileId : IEquatable + public readonly struct IccProfileId : IEquatable { /// /// A profile ID with all values set to zero diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs index a10c55f4e4..c786a0fd45 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// Associates a normalized device code with a measurement value /// - internal struct IccResponseNumber : IEquatable + internal readonly struct IccResponseNumber : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs index f41858f303..e1f1bb32fe 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// A single channel of a /// - internal struct IccScreeningChannel : IEquatable + internal readonly struct IccScreeningChannel : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs index 5464de9c5f..7cb5c7901e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// Entry of ICC tag table /// - internal struct IccTagTableEntry : IEquatable + internal readonly struct IccTagTableEntry : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Numerics/ValueSize.cs b/src/ImageSharp/Numerics/ValueSize.cs index 659e0ebfe1..fcf61a586d 100644 --- a/src/ImageSharp/Numerics/ValueSize.cs +++ b/src/ImageSharp/Numerics/ValueSize.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp /// /// Represents a value in relation to a value on the image /// - internal struct ValueSize : IEquatable + internal readonly struct ValueSize : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs b/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs index e3b9c11bdf..07045bb5ab 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Represents a composite pair of pixels. Used for caching color distance lookups. /// /// The pixel format. - internal struct PixelPair : IEquatable> + internal readonly struct PixelPair : IEquatable> where TPixel : struct, IPixel { /// From 0bcb523c37b74da55820a7f7fabb0d8e679b097f Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Wed, 28 Feb 2018 10:42:24 -0800 Subject: [PATCH 09/20] Work around compiler bug --- .../Common/Decoder/ColorConverters/JpegColorConverter.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs index 23c2071cc6..0427289061 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs @@ -63,10 +63,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters private static JpegColorConverter GetYCbCrConverter() => FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd(); + /// /// A stack-only struct to reference the input buffers using -s. /// - public ref struct ComponentValues +#pragma warning disable SA1206 // Declaration keywords should follow order + public readonly ref struct ComponentValues +#pragma warning restore SA1206 // Declaration keywords should follow order { /// /// The component count From 2fef2b1f1e2b051d03ff472bd6d8d844ca2bef95 Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Wed, 28 Feb 2018 11:38:48 -0800 Subject: [PATCH 10/20] Remove rouge blank line --- .../Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs index 0427289061..187b65f72b 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs @@ -63,7 +63,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters private static JpegColorConverter GetYCbCrConverter() => FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd(); - /// /// A stack-only struct to reference the input buffers using -s. /// From 7c69caa8ed280f6927922680f59ea7a9fa9d6706 Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Wed, 28 Feb 2018 17:19:51 -0800 Subject: [PATCH 11/20] Update T4 templates --- .../Generated/PixelOperations{TPixel}.Generated.cs | 2 +- .../Generated/PixelOperations{TPixel}.Generated.tt | 13 ++++++++----- .../Generated/Rgba32.PixelOperations.Generated.cs | 2 +- .../Generated/Rgba32.PixelOperations.Generated.tt | 12 ++++++------ 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs index 964fa98f95..9505ee6cf7 100644 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.PixelFormats using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - + public partial class PixelOperations { diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt index aa88b6606c..e23f196212 100644 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt @@ -4,9 +4,11 @@ #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> +<#@ assembly name="$(NuGetPackageRoot)/system.memory/4.5.0-preview1-26216-02/lib/netstandard2.0/System.Memory.dll" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.Runtime.InteropServices" #> <#@ output extension=".cs" #> <# void GenerateToDestFormatMethods(string pixelType) @@ -24,8 +26,8 @@ { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); - ref <#=pixelType#> destBaseRef = ref dest.DangerousGetPinnableReference(); + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref <#=pixelType#> destBaseRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -64,8 +66,8 @@ { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref <#=pixelType#> sourceRef = ref source.DangerousGetPinnableReference(); - ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + ref <#=pixelType#> sourceRef = ref MemoryMarshal.GetReference(source); + ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); Rgba32 rgba = new Rgba32(0, 0, 0, 255); @@ -101,7 +103,8 @@ namespace SixLabors.ImageSharp.PixelFormats { using System; using System.Runtime.CompilerServices; - + using System.Runtime.InteropServices; + public partial class PixelOperations { <# diff --git a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs index 43060539dc..edf6a88e1f 100644 --- a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; - + /// /// Provides optimized overrides for bulk operations. /// diff --git a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt index 9d22293947..d83e49f770 100644 --- a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt @@ -18,8 +18,8 @@ { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref <#=pixelType#> sourceRef = ref source.DangerousGetPinnableReference(); - ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference(); + ref <#=pixelType#> sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < count; i++) { @@ -40,8 +40,8 @@ { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference(); - ref <#=pixelType#> destRef = ref dest.DangerousGetPinnableReference(); + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref <#=pixelType#> destRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -61,9 +61,9 @@ namespace SixLabors.ImageSharp { using System; using System.Runtime.CompilerServices; - + using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; - + /// /// Provides optimized overrides for bulk operations. /// From 411eb420d035103e16bfa5fd40a3198c477d840d Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Wed, 28 Feb 2018 17:24:50 -0800 Subject: [PATCH 12/20] Remove T4 reference --- .../PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt index e23f196212..365f5cb514 100644 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt @@ -4,7 +4,6 @@ #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> -<#@ assembly name="$(NuGetPackageRoot)/system.memory/4.5.0-preview1-26216-02/lib/netstandard2.0/System.Memory.dll" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> From 73b4da85019369a09bd923c44c4af28cf9b8ad39 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Thu, 1 Mar 2018 09:39:05 +0100 Subject: [PATCH 13/20] Added a convenience copy constructor. --- src/ImageSharp/Configuration.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 0f69194f2d..771e2007fd 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -33,6 +33,18 @@ namespace SixLabors.ImageSharp { } + /// + /// Initializes a new instance of the class. + /// + /// A configuration instance to be copied + public Configuration(Configuration configuration) + { + this.ParallelOptions = configuration.ParallelOptions; + this.ImageFormatsManager = configuration.ImageFormatsManager; + this.MemoryManager = configuration.MemoryManager; + this.ImageOperationsProvider = configuration.ImageOperationsProvider; + } + /// /// Initializes a new instance of the class. /// From 3b76e7f7671c01a0426d1601ec0b62164cb2673e Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Thu, 1 Mar 2018 13:33:10 +0100 Subject: [PATCH 14/20] added missing property in copy constructor. --- src/ImageSharp/Configuration.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 771e2007fd..afa63dacbf 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -43,6 +43,10 @@ namespace SixLabors.ImageSharp this.ImageFormatsManager = configuration.ImageFormatsManager; this.MemoryManager = configuration.MemoryManager; this.ImageOperationsProvider = configuration.ImageOperationsProvider; + + #if !NETSTANDARD1_1 + this.FileSystem = configuration.FileSystem; + #endif } /// From a8d54f212ea3dd85979bd25f6dd0a9255874d0c7 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Thu, 1 Mar 2018 15:43:36 +0100 Subject: [PATCH 15/20] whitespaces removed --- src/ImageSharp/Configuration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index b0e414c2fe..5912ac6309 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp #if !NETSTANDARD1_1 this.FileSystem = configuration.FileSystem; #endif - + this.ReadOrigin = configuration.ReadOrigin; } @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp /// Gets the currently registered s. /// public IEnumerable ImageFormats => this.ImageFormatsManager.ImageFormats; - + /// /// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source. /// From 766481c18e719d44efff926a61432ec05757c66b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 Mar 2018 17:19:51 +1100 Subject: [PATCH 16/20] Can now read padded RSTn markers. Fix #481 --- .../Components/Decoder/OrigJpegScanDecoder.cs | 28 +++++++++++++++--- .../PdfJsPort/Components/PdfJsScanDecoder.cs | 2 +- .../Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs | 2 +- .../Formats/Jpg/JpegDecoderTests.cs | 6 ++-- tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/External | 2 +- tests/Images/Input/Jpg/baseline/badrst.jpg | Bin 0 -> 74497 bytes 7 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 tests/Images/Input/Jpg/baseline/badrst.jpg diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs index 67abba9f33..7d58d168a6 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs @@ -197,16 +197,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < decoder.TotalMCUCount) { - // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input, - // but this one assumes well-formed input, and hence the restart marker follows immediately. + // Attempt to look for RST[0-7] markers to resynchronize from corrupt input. if (!decoder.InputProcessor.ReachedEOF) { decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2); if (decoder.InputProcessor.CheckEOFEnsureNoError()) { - if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst) + if (decoder.Temp[0] != 0xFF || decoder.Temp[1] != expectedRst) { - throw new ImageFormatException("Bad RST marker"); + bool invalidRst = true; + + // Most jpeg's containing well-formed input will have a RST[0-7] marker following immediately + // but some, see Issue #481, contain padding bytes "0xFF" before the RST[0-7] marker. + // If we identify that case we attempt to read until we have bypassed the padded bytes. + // We then check again for our RST marker and throw if invalid. + // No other methods are attempted to resynchronize from corrupt input. + if (decoder.Temp[0] == 0xFF && decoder.Temp[1] == 0xFF) + { + while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError()) + { + decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1); + } + + // Have we found a valid restart marker? + invalidRst = decoder.Temp[0] != expectedRst; + } + + if (invalidRst) + { + throw new ImageFormatException("Bad RST marker"); + } } expectedRst++; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs index 9e245ea2c6..c6f6ac270f 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs @@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components ushort marker = fileMarker.Marker; - // RSTn - We've alread read the bytes and altered the position so no need to skip + // RSTn - We've already read the bytes and altered the position so no need to skip if (marker >= PdfJsJpegConstants.Markers.RST0 && marker <= PdfJsJpegConstants.Markers.RST7) { continue; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index 4fa0bc281d..54e2833b11 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, (int)stream.Length - 2); } - marker[1] = (byte)value; + marker[1] = (byte)suffix; } return new PdfJsFileMarker((ushort)((marker[0] << 8) | marker[1]), (int)(stream.Position - 2)); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 139fa351bb..95ee40e807 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -42,7 +42,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Bad.BadEOF, TestImages.Jpeg.Issues.MultiHuffmanBaseline394, - TestImages.Jpeg.Baseline.MultiScanBaselineCMYK + TestImages.Jpeg.Baseline.MultiScanBaselineCMYK, + TestImages.Jpeg.Baseline.Bad.BadRST }; public static string[] ProgressiveTestJpegs = @@ -61,6 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [TestImages.Jpeg.Baseline.Calliphora] = 0.00002f / 100, [TestImages.Jpeg.Baseline.Bad.BadEOF] = 0.38f / 100, [TestImages.Jpeg.Baseline.Testorig420] = 0.38f / 100, + [TestImages.Jpeg.Baseline.Bad.BadRST] = 0.0589f / 100, // Progressive: [TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159] = 0.34f / 100, @@ -119,7 +121,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } public const string DecodeBaselineJpegOutputName = "DecodeBaselineJpeg"; - + [Theory] [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes, false)] [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes, true)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index f1f989581f..db469f87e1 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -96,6 +96,7 @@ namespace SixLabors.ImageSharp.Tests public static class Bad { public const string BadEOF = "Jpg/baseline/badeof.jpg"; + public const string BadRST = "Jpg/baseline/badrst.jpg"; } public const string Cmyk = "Jpg/baseline/cmyk.jpg"; diff --git a/tests/Images/External b/tests/Images/External index 8714b94dc4..653f0c7e3c 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 8714b94dc4bab6788fcbb6254174db2b9c8f69c9 +Subproject commit 653f0c7e3c84657f68dd46e5a380186b3696b956 diff --git a/tests/Images/Input/Jpg/baseline/badrst.jpg b/tests/Images/Input/Jpg/baseline/badrst.jpg new file mode 100644 index 0000000000000000000000000000000000000000..98e3ebc004bed166d4b971def3eb7509432b655f GIT binary patch literal 74497 zcmeFZXH-+&+AbVw=pE@DrHT-Wp(gYqH4s2V1ObsKB_Lh8pmah}#L#;$(wh_^6zRPR z1VliP-oY==e)rz*_nmQmeEa-3W1MlGnXHVNnS0GO?`y3!=QXc;X8oD@vkaio(a_ca z;Njr`tZ^^EpB0`Ob#KRK0DztzKo|f3kN^nq=l}$`93Jikch0OAZN zaNj`y08JhH)<5iVAAhI65%?Q{zY+Kwfxi*>8-f2T5g>{u0N~?1XTULo=y<|^=Q9&f z|Hu=F%K!ig3ICn{Z#F>^^cP}(vx&b@_&fcLz~2b`jlkas{Efi>y$FEBC1m8pL2@84 zw}gb8#CG;zw;D$g8#I^F%*2fe<>QY z4EXr}%F}%KC)(Ekwf|q*2mtTT%%6FHDu9HT7)VS+0t5m{NlC~k7$_;o$thUq=&2bv zSh+YkSlQXR`Nagdc}4ly*#++liHb`|Nl9@D$SQ(C3SyE{puZ}?BPAuJAg5raq+|y1 zu=9Za+;aW+AQ*-_jbpDy}@CgWsh=C-eWaK!5 zS{j_k1O)hm1VluHI4K9uANMdr|p{b>n zZ|Iw_@VD%M*8aia(ecUY+4;p^e&OMCsDGvZa_oQbix%e>J|Q6iA@DE1@bJBG z8v!jL5sw5how5M7Q$mAdl%`6_Xb9*9;9m&Y0aEBuLsC94l1VIInadj$5yKfYbEQO6w~Uf6}#O9 zn4l23QBX!S1q#0K8v8Ra51`hWWur?>s~ssSA@Ly6T1;sEIn8p-a!vcGEUc(EIg;hC z9rG;s^Mv6-uoQ+hi#!Pv*f*8ng%KjgwA-i&VpCvD7t4h)M}W;a%pwaVUKlxPPRy9U zlaE)IL8kd=~m>HKk^BJ=AVJwCB z3#qhK@Y(grRh6J6g-myXaw3jB4N$sZ( zb9QAJHB8FE&Dpa!a9|jVY!)4p(#z>vCu85b)?>)I+MktD%d|~(o#S^A^p6#m3;_ZmqJ%W&Ui2F&jp=N9z)O z-72&dQq8`GS9q6!M`mDBq^;LHP{M zb4gmt3~Ud)I9{^mG!FC*{0_PH^-5aGImqSd6gcMk@CRU(YfgPSfAj}Hrh4ZQ@;mah zuju`(UwJ^w#Dc?@79U2H)rAYQMBuZRlOI1xDNU-Fmz0Bea+G$m>LT(QQo*(yR+?lG zN%Oa>#HfP#rH6AEr7VTb&*EYzOrm9B6;FyI)wR2N-p{{k(& zUKaOGY^|ZP<^A_TtN64eE}c#Jd3 zG}#gL!Z6(;(;JPnQmyy(UhnDZQGEH-e8m}^O->@+;e0 z#tPo1PY;liTBe71{WN4~uO9HA#}|i_@_ysiKY;RZdX?*pIrQD@5=~yx_w#cRwANwk zuAbTN!AFef0()fc(+8`c%&Yn8lHc8&lix^ZWxgOUdn{f32&DuneCw&{yKyr8tR-9{ zru6%4@BZBO^ptC7*f(9Wa)7L}HVl#mZ+q1GFsHdK_Fz}r2;XWb8fulpj!h4|SLT-2 zemR@m`hH6|D|Bo(KI!r?`|^_DkKafBe#51 zl&p7ehT%cr%v=(J?lvB@z1J$^bW&9;u_XAW^!`Zj^$SKT(XlDP2e~gOe8P=8*yX6c zggHDu^D}MIi=mizM@tn4CPW6zabTX=2HG(AtTa4}HP3J+rnU|&qutzmZlz6~Dgz20 zCoD3;Ok7O$ThM4U*%cQ2wtqVqM#wb{$wO=!*L{@iYXVYbJ+z}_=4L--tYLR?yX_`Y z@**n!i-Wg?bXxwwl;tsb5V5!%*I2U))t1rsxo-mGE>dpnJ-yO!Kc`&ZeNC#>z2V@;hmPu28(#nZjk8&t(lQx`HH z4d%|gFR3INM$y>m?T3?zruOP0j0(EEP60gx6e{xJN>6Ds#ADq+iB*G`K4ePil9SKJGk}b9!lP#_G(F0FoSu zJ`hq}{}3AD_3Elb9+Xh^gEhT^-FGOP<#Cnk8Y*@nP-plPJW}f+qq!vL_~J+FPn!&3 zu)UFG{dF?}xR7|jh27CheDdaAAARD_8OA`<_||M*e*`g9ujtutw6zDWJG7Z}s(*W{ zq5&B{gHif!(Xf)Y@WF`Ha|!gGu5@HUn;VaStt`hGIdZSQBF}yiQ8%3+kI7eONaPsi z)8k9f=1$K^%?q!~_Rw;w^_!QWrnuCJ1!ZD?V0;}XTo|-&ZK(~dzW)4~pX;S(0V|Eg z%m<_D(x_%H0eSQqdLuegHRLPRTHN3Zb%r{g=H!-$iIr@W`&NGQm$ys8H6wH<-(h-F zySLOj?du(YTv=Bl-%41(KRr51w)~{O%u16Brjbh-Yw*YH_Y(RYDU5&8s(fy;(p8|E zUJ$GGd#v$&bB>-V2Ha$=Ei>p5v2e@8RdyQ#42{e~Qq(0B59P`%w)>fAyV#l!H0l_n z&D@hu*%MkxHo{Wf7h~8_2<_G}6W)khYD~CTzu0{Su|fc}=6~0pPmgxl`M4k@4{iv~ zS^YS_Y)q9Zr+>}jT6^u%rL!aqN!+S8CFFv5WEpx8IHslA^U zv}2jTbUTOx%(YSA`>v!pc3qKs^8+2fBg2TLkb{uik-P!m(&Hj>uxan}^GwS}15%qp z*ULyWXXEhsYQBtDl&QVUc;gSp6%Oa0!=k>g+bm@d`E6>IvC-+IYpx=+J9$y${4b*$ z2*ok770&4KQOU4;6+`UaO*pz23fY*dReNU=eie9Dtou6i51=~mh*l49e=+ck>5!py ze(MilEIwl4_26;fnjis)+LP;o68iO>|8RDokx^1-bl|JNpC;u*QZw!B@P@Sz%7Ouk z?~543p|PBP95n6b>oOW}j>&)FCQFVt%$Vy*{Bm>ddMRC{G${Id>SthQlOm+M&J48E z=aEkRxA(*_MUS<4Q;)w@ClD zO+(RL3a9%kt?$$PHW1_f`QKBfxSPEasK(P4t0`SxaFSL{C`cj;Hf3Z^%7^$h*Wxvc zWCf!4cVASkqVw#l;y!C5yl<*jE?epBW7kS#ezAKHI>>Ux*r5&r+YN3)MB3bqib$?1e9=zkl+vt?UmVbFBDk`{v*e;HPw2JK}X=S@a4+#<$4_5kb1t z2HOu!(ojf?kEVy#%afnEjnF|NVQjyKLc2ao#p|}>xqMh^BKZz*IOukwjIa;pQLwgM zLaT6+#pNS@@xJhzefjB?WFObaYTexy`#`dtMElNI@hjFLlZi-JRRL4%OaAtx2Lu}l zK(4rY##$$zg6}*MdZd8x^B{qIInLv#&__ubPubFLyBw30ucupgDs~WDHGe2PD$kxe zn44RS;thM?F5&8pvE6zh)5Kfh|Bcdg2=mpe%uzbJIE+a$lba^MDR-J^mHI8s=IK`z zyG#C{`1a6sI#-6ZwbkhJ8>?U)t_VlIzdCrGmy$L;}No z$)DjxeD{rmU(F<&Yq1AqiuWKbC%>*yW*%zRBqgtr2`Q5Kbd(tCDs4wcCalF~=q6J< zFN7YQl%+gLApKDvTR8ZBG=jM(Ib}~%r;3_cQC%W(Ba6YDBsWB;@!s2GAo0ib#Yd=h znYD{iYbl#a=M|QRX~>zXGLqK@h4^{S?zK9Afplsk*k*T(_6MgH+rmfZmKrza!c@LR zzXJ2LnIZ}rE~TD{D-F#WTCQMp?-!jGzB0xBkmjqcX-jaa`GpkOB}9kyVpfI=3d_jl z$^F3JJTKz~p4mnYtXSsl?&{Tir8)(R1i53z8itw9(g!q6k~u7Ph-pohufbbNwZd{g zDSvvmRh2AkO;|CDOi`lw3 z-Sf6_y(b|ieh;7k@piSbePREK`>DNyqqCyG@8&N8+>UmN0%p>B;(D&C_Rk%)eBJDg zef3RjeP7th+6h3ED9IGO<-DC-o$O!PaC?-%mAxkpx(5Q?#aY~S_i=t@<9*lJo$sF&sM)*Q zx;eVOa&&R#{;NWpr!F3^6nQ;79PQ+u**ueeYG)^X_o8+q|+@!);1`2}yUwL3cr5lYfazV&c;G z74H2Hb_)0YYS{m)p-TT%g7Y1h`!`ZJ*tq{Uq`wdHAA$TgTz|v$A0hA`8UKBC{SDWD zgus7f{P)%Me;8c<&9>~FaTLo7$EE%eHzL5t|LcuQgt#{m$zKr|A~GP5n1q6if`Xik zoSc%1j)szomWrI5<`xYtJp&^nBLy`R^DPEuItE6Df5eRlaQhGvkrEM+GEkCJGW^Hs zA8{kRW&9ulJl?fwQe-}fCO(E_KJg< zPfAL2Su1$m0*zojCLY5aZ}Z?Dgy|Ayc9vX-5JM779tqjY%+QQ3jxACq$L+DQ-bR7* zNk;~|NKgEdaz07t*R|kOVq*_1U%gI<`WnS#BNP7txRAq5k>u#N z4z}^(Ugid8`=kR=#PIs2Y5(=F(?S;aogFP&f)HP9Y6zTJ!C`)^v>KhR4#iN&UW_n_ z$az8XRJI%b0AkEvb==8vC0*W=HzWyB4PJPf=?q}G67DLewxULaB?62qd%(Sgo_jvS zQ~B;YA&6W2vEIKBFA4QQK(JCR%S(#%uCcs$hoAB$&bfuBStY?5Q{o|e1pNzDR}bk7 ze*8$+z~hHIDY=0D03HV=HnA0^P(Q1E%T=7(@LldQl{e_!^|zyw!Z?8olOZKTNHR#@ zz>wtHF_=8Wtl$en0!6uZLFESDbe@a~DR8>%7QOQSD+5vbpsH1OCziITD$i?tE z-Y4(gAz$QNs*h|ta49$5xra)zsOSaHab1cF;<*J(ZA??gC*CdxlA69%{^8! z73PL1s8kfv>dlX*S#}eeA#j0A!0c!T!T5lVjj{M5LW1+LN?I*q?;b(I(4B4l4nsGg ztN`amMs0vNALRaq9(l8?qCY}~fGTOyl4~3KmBb$+2Q7%#v^qK_(iVt>%@FXr>wgoI z4mL_}JkAee#snhbW8aoZ2Mh2qs?eHnE1*yF)E6WZBVn#Hm?t`>&>XJ*VDsni=wp|{ zgF7JKV3jik)3q#omB5Dl_^&2Iz6o-yKFk2G9bH*&+fbPJ;%2I-2r5DK)y7hC$A;pC zN$%Q7Hwzem8urcB5IpyPqiOj zkdQV8s~*JY>SY+}It*46YG0;s5U3^6lCKU(8l$&nDB-{)jzk)kK8l6~Uk-g-O;+wb zE!K($DsYk<708M30PIfljA|o*+xi|9`ru*YQmRE#+^ARd4I_ajhPNWAe&;<$vz55xV4B@nx+b(ncAhN3>5FaZP-K+ui55EweCm^0JEP;<1s5rq#xV2 z3nOPH4B9uODa=H)TFC>FEsqtIYVA8H^pv^#u+gK|!o4yfl+b}7?<>T7LBBqtB{cTR zxRUWxhA~DbED2v9!dlq{Suuk2BO`%sH94+7GLh-E&s5t_3Lkor*|;^buciznxKHH^ zYxCu4Y}dBx-Fc(dMFj#%6WsA0eCTR^v6}OIf8436t_U_p&_z)){SgbiW0p! zKu6gE+TgMHgH{rb%^7+=5ez#Q!qJi|PodnG0>;SC36T?JVkL%jeyUIK_b=*yQ$H3T z)52L}F2-ZIoSq$jBodoeG7z)FLc`mC`iaTj)bF-1S-4h=$T}b?wHW?t7F;eNUAh9 z+M&S0xXUiu&UjQ~sS$$!SU>)z?-_hCmc5cf1K1)dkQu=f1?)N!{B}ux_!bH+Zg`8$ zFx1pRvRKld!-Tc8fC%5yZg5w^eM!&-m7qRWbCVu};mu4?2s|!6EZ3YWY4JX-5S+;2 zUEl8!Zb9u2TD|H8Z`7JSKlK+eQPv(w9FGhh2j*FM?**1Y_t%gnDGYXT5Y=r%1#3U} zw5dQIbQD`*(VY17QvoAkM1p_uuae-%3f^4-zDRZR9A0Z{s#+@(MR0FPb%+5YIi}`l zd6Lc$CGNgys3nQnHp^wkg^5bH8<5aQkRSnvq@mVF&aT==9_O)sCUj!hEkXRm3I&P@ zF|^+#COQFmZbEk_kW80MAB?*N7r{fovGV?m4`pT^goj#WVl6*1y*rd{jy<6ym<@QY zmE(e!P>Y?LzGfM;OZG^1+l&Rud{lgJPZq;b*otw>5mS^j1T$Yu=3Du!;+B4B$mWV9 z^{=HvSi=&zPX;aQ`J$1yu{}k>9m;6;)4dzoC2)0UYHz5Gjq@$vt~`*~)NePl@57=e z=m5mn>3qsZ)+7RZh5S|^4L#n-Aux`MxB^Oa8%M zytZ}i`Ay16vH3pvsCw{~V=yU;0`*XiP_!#BU+5RJ4 zJWeM*29qu>H~x@>Z+ybIk~SXNBg-L3kci$W7sC7)l1v<*`_${A99v^fEWv%W4P{{& zUeQr@*~_&J4jwVGrire%64E}xDAmwCaNrOm)8r4p4}Xi42|gQRq(COZXhWm@#^^)Q zwo-2!nGH4c0Su#E*N!V*oO03n?TnNjnSZ+HZ#wG^}a^v=Z>t@yOsKu3b~B%bqk1&njR zR=hKbXcgaHg40GH#PLhSf~dGcgyLd?szcw;VYowAD`R=h$g5Ib8kN++s%cSurSB9??@r<>o~{#;cSviKY3(KZE`Q z5EhdPNRSIe87jO!G@Au!a>Xd~+OZxuBOH2Dl#&V4kddvtb`w-VTm}}SbVjIvC+_fm zbfQ_VYI-sOrUo~znn!}}2$R|h)FE-Vp{2y{)C%51lay4!J{GPR=;ol6o$z&~AKZrt zP4=`=M;$vc=}6CwPbGYrFWYT~0y$=}j0+gWCR3o6;hRi}>9vos9ZJbi+`(+@SQgjOOLz6L5?^0;q?9lmU#Bz1Q3jLiBv8 zp9ML>-Hn>oF8u}ZKM7JOa|i?bY*XvDnD=|+CqR@1ih2aXXkvv8Y5E(uO%9oxk94xU z9B}wCv2H({8Og|%9a;UAvvE+hpqD=b9@SzN zMElEW88Ym(ZaPcs23*l05UIwe@;#NNuUiU@tTd@quuiX~d0c>+sz5R>?DDmNyFtp~ zH_7txr8o)!B4@uzGADT=fSPU<7iY)Iut+Kr2tmlesE}uqGbAb2dwwQ%YZqL`#;8Al z?fhhn(R>*|4GKixF!V6nvV-w1(3=<#u${eo;axKOeA#w62TUlvmakdfxz zpJrHYjWgEL???<@_<^RvCTn1dR+HNu^|cg>`k|ERIa8r*TGWG~H@$xVtVT3eIwAg# zQvHG^U6p&xW7`8!&_i+uvPNt=Lhnt6g|~6KgnG!@BbB(6gg_pBvce4Euwf*Okw{

xpUQrF@gUoYRCKXbrWMO}A$*`T|6vdT~}z}I*Z1Og9Ri1zW)R*JI3 zT@gM_<|FEZU!50OX<;p!Njk81yLpH3sqb>)Xs(C%)dk$DT@d(u1HGDS*V{MTDlU3J z#cOi3v=Dk07(-{Fk2_;x{X#d)I3g+ zWFKtbY4L6-OFoehy+Apz%|_?dk5Xreg10~_VLx4#rR_oS zua-Xmi&kFx#12My&#I;lU4vzT@G@7RPLc2mZqhxJo9;enma4#+tWDGpD-;MGG))XY z9?r9jr!%O9ViPfAQAGXv;uysyxEGANKC2{AsJ-yECAEqWzL1vWkoHNTy;WBQ(sa#A zc{3ep*xN$kl{;ctz`LX9dok$+omJR?q**4}p(h=cZ1i}tb3NVf1)3__;DFuIq`!~aell{6c-{sPQ*ygfCyFz3+geM|~Ar|FbEZY|R zA}H|O#yMHUJD9K!F>#<=*u;_4FU9Cj4724gpwXfsbI7xtf}63DXwdmE!^DQ^NATR# z)^h9@5melj-*M4HSJ}cyHalGaSRlj5umNM~J~A;w=&@#^?xy!R%uNPxPhn%cR^kJ| zmfhFn8v((E$;&WJNb)Vl{%;cLM^h{ptjS4-81B(}jxVfP3*{Pe6u5I@i_eyiSJa8! z&zB3GpiiagO>y7#cMOvW9>IgfP=Uqi|+! zj-`RV{P=PO3e+%1(O0{`Fe$ph&LGHK9_{;=l3ZQE&@fLEquz~H!Ix;^s#6TZu`n{t4aNqudC$UkGRSdLsp6AT$PM| z3TaWiaX=sEC#4M&gE5%{T$}n26zuw>5>WFz(5tGetuR;7s<^aC$P##vRPgmn(|FD; z@YSH4{PXOmwqbm>efCC)#ta*>3K!#*mccn6LkGTsm!=-KGg3Dqpjpk3}l#J$@z>DY(vN za+aRLz@h@jDkjB+`nU*V-QlwmeUPxnujbE8=9oz<9dRq;3XP&+#Yv;{Gfq-TBl0RTwn{d16wLpu}1W*^F zsn$NKSE*L}UFK$A@c~bd-eZ&xZZ6-84h%D7$TiH#DlShs!SM=L*aWElsGPgT^0n%3LeZ#u& zS$gte-x6UupFt_mr+kC6NiWsDk_4fSlj2=c_Tzl1{&G?XPN!P{L-HObhu~|I0Acz7 z#*K@&aTN-xelp3+&C-+QhRGSmiR+(hj|T!-P8iX-*)TJimItdb_d4rBu&|Z~9=3H- zTL<^ED_}3u_JFa!UO!|!0@T*!e*g{K&Yk(bzCoe^hVT}Ge+J5#?r~Iv9krT0o^;Om zYQ+;f<`}T()32gJN2omtm5y6+d3$ohefDVOMX2QF+?s2jhf7h{*T`YXO3Lf?4t6dJ ztcU%etzCjXKh;?TF*IeM~K!|LOei$MJ-m}-sT6+;!cy9MOWl* zbW(Jq7TM+DQ6Y=Z?@-gnz9GT_>y>(j(b&(IkD1c`0PGV9CVWsE$CwRchF;l0sO_`q z1gLA(tX9F+n40EI2x2@$Pgkvfw?QEQ&bB{{NLQ}sAkwhyP`oaAK=P(SDQQYIUb)DC zIHiky)S7!v%NbLv(d(xFyR#HLd|WsS9&*>@MBuumT;z;bw&Gw$Vu2?(&yJ_G!E$W`$E5&@j`LLd8&^z`vg!xIV7U{7J00A zzT^PcNsR1=;2}+G$KvWLqDT#i(E!_PiHq@Qp0|UI*D3qwWRBuA)qFc*jBocmARjDD z+`34jzyGFexZ2#;;Eg7NL*)g#Ap~A8{{ZA(m#Qh5jB&N0;!ZRSegt-YI$>OR&{FL_ ziVi-fpdKnE5=O#MJ@OZmZehKnw{G*82y!G@8&_(`p~tIYsbvcYhah=MCIq(8V%onA zX1v&sF}|LiCP!C7Itk+XKH2T!8YXtOcapLefKT$Y07J-*<2(}2HDi473`lIjgWI5BWuiXJWk6j5xV@p=^l-lO zsESFCt2ak+bI4*7eABE=xTPd@l&t#}2Lc$!jg>7)5TjBQ2I@;%SgPZu0W)wrFJ3Fe%fIO*4M>Nk=BY4O~kX$6~Ji;9idlt`JzEm-~MJ)5W_VWruaL80;+_Dmb=k zqC&SQpS&7EUcevX?dajDs2?*I^6UFPEAP)eg!iN7O3CQTB{9;b>Rq<+A{usm3UEz* z{pXDRFbdAu;)8PKlJbwkhs6i^nwEZsAoM%3#OT!Wfy6{JJ9fAORFgvxAB~Q-&G0SW zp1`t{RNqgRIeV*v&2H&-8|<#}be~mq{rGyR;A=*k%qyF{ zXBm{;LuG|2yB;wHMs4ve)#qAj<{XTw@7oy%VFhA6Rd3x6ydOL{3lA7wopPd96?hfE zRhxJ^PSvxjJPui8oU@pjck~pWHGiaP>p;_~j2{N)nwV@=Ibb7ySYBQbyF}9Ac;{nh zaU9G5zoo+JtjwGQgu%YwtoQqa|d5>dT$RwIcD$3ApX5y@? ztUJ$}$uy`pCN&V~nD28cP z)x>rE!$nIB9p<`+ww)?4Wwn7P5?1IUtfzMJo9^scSGR@xLj=w5=+~qDE!y;+c8W1> z?^$oMmYaXRilZAiiF!yip!kAIG(F

7(z5$K`K3+GNMByz;Iq^pk!YDr`R#zx^iT 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ᥣ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%0Dc_> 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 z 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 Tb_yt>nnOtc|NsC0|NsBlzwlwm literal 0 HcmV?d00001 From e3d20c7753512531d8603472ca3a3f4a37974712 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 Mar 2018 17:32:29 +1100 Subject: [PATCH 17/20] Check and ensure no error --- .../Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs index 7d58d168a6..2e1372c564 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs @@ -217,6 +217,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError()) { decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1); + if (!decoder.InputProcessor.CheckEOFEnsureNoError()) + { + break; + } } // Have we found a valid restart marker? From 494fc9ab24b3a5952ba473058e4a95c44cc3db95 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Fri, 2 Mar 2018 10:00:41 +0100 Subject: [PATCH 18/20] replaced Configuration copy constructor with a ShallowCopy method. --- src/ImageSharp/Configuration.cs | 42 +++++++++++--------- tests/ImageSharp.Tests/ConfigurationTests.cs | 6 ++- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 5912ac6309..8d3811a974 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -33,24 +33,6 @@ namespace SixLabors.ImageSharp { } - ///

- /// Initializes a new instance of the class. - /// - /// A configuration instance to be copied - public Configuration(Configuration configuration) - { - this.ParallelOptions = configuration.ParallelOptions; - this.ImageFormatsManager = configuration.ImageFormatsManager; - this.MemoryManager = configuration.MemoryManager; - this.ImageOperationsProvider = configuration.ImageOperationsProvider; - - #if !NETSTANDARD1_1 - this.FileSystem = configuration.FileSystem; - #endif - - this.ReadOrigin = configuration.ReadOrigin; - } - /// /// Initializes a new instance of the class. /// @@ -74,7 +56,7 @@ namespace SixLabors.ImageSharp /// /// Gets the global parallel options for processing tasks in parallel. /// - public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; + public ParallelOptions ParallelOptions { get; private set; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; /// /// Gets the currently registered s. @@ -138,6 +120,28 @@ namespace SixLabors.ImageSharp configuration.Configure(this); } + /// + /// Creates a shallow copy of the + /// + /// A new configuration instance + public Configuration ShallowCopy() + { + Configuration cfg = new Configuration(); + + cfg.ParallelOptions = this.ParallelOptions; + cfg.ImageFormatsManager = this.ImageFormatsManager; + cfg.MemoryManager = this.MemoryManager; + cfg.ImageOperationsProvider = this.ImageOperationsProvider; + +#if !NETSTANDARD1_1 + cfg.FileSystem = this.FileSystem; +#endif + + cfg.ReadOrigin = this.ReadOrigin; + + return cfg; + } + /// /// Registers a new format provider. /// diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 06f02fcf16..d38556fa9e 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -22,8 +22,10 @@ namespace SixLabors.ImageSharp.Tests public Configuration DefaultConfiguration { get; private set; } public ConfigurationTests() - { - this.DefaultConfiguration = Configuration.CreateDefaultInstance(); + { + // the shallow copy of configuration should behave exactly like the default configuration, + // so by using the copy, we test both the default and the copy. + this.DefaultConfiguration = Configuration.CreateDefaultInstance().ShallowCopy(); this.ConfigurationEmpty = new Configuration(); } From ced881cae98e5ef5e4c73073e02cfdb0a79dbe20 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Fri, 2 Mar 2018 10:15:42 +0100 Subject: [PATCH 19/20] removed ImageFormat redirect methods from Configuration. --- src/ImageSharp/Configuration.cs | 121 ++---------------- .../Formats/Bmp/BmpConfigurationModule.cs | 6 +- src/ImageSharp/Formats/Bmp/ImageExtensions.cs | 2 +- .../Formats/Gif/GifConfigurationModule.cs | 6 +- src/ImageSharp/Formats/Gif/ImageExtensions.cs | 2 +- .../Formats/Jpeg/ImageExtensions.cs | 2 +- .../Formats/Jpeg/JpegConfigurationModule.cs | 6 +- src/ImageSharp/Formats/Png/ImageExtensions.cs | 2 +- .../Formats/Png/PngConfigurationModule.cs | 6 +- src/ImageSharp/Image/Image.Decode.cs | 4 +- src/ImageSharp/Image/Image.FromStream.cs | 2 +- src/ImageSharp/Image/ImageExtensions.cs | 10 +- tests/ImageSharp.Tests/ConfigurationTests.cs | 83 +----------- .../Image/ImageDiscoverMimeType.cs | 2 +- .../ImageSharp.Tests/Image/ImageLoadTests.cs | 4 +- .../ImageSharp.Tests/Image/ImageSaveTests.cs | 4 +- tests/ImageSharp.Tests/TestFormat.cs | 6 +- .../TestUtilities/TestEnvironment.Formats.cs | 12 +- .../TestUtilities/TestImageExtensions.cs | 2 +- 19 files changed, 49 insertions(+), 233 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 8d3811a974..d41e48678b 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -83,21 +83,6 @@ namespace SixLabors.ImageSharp /// internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize; - /// - /// Gets the currently registered s. - /// - internal IEnumerable FormatDetectors => this.ImageFormatsManager.FormatDetectors; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageDecoders => this.ImageFormatsManager.ImageDecoders; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageEncoders => this.ImageFormatsManager.ImageEncoders; - #if !NETSTANDARD1_1 /// /// Gets or sets the filesystem helper for accessing the local file system. @@ -126,106 +111,18 @@ namespace SixLabors.ImageSharp /// A new configuration instance public Configuration ShallowCopy() { - Configuration cfg = new Configuration(); - - cfg.ParallelOptions = this.ParallelOptions; - cfg.ImageFormatsManager = this.ImageFormatsManager; - cfg.MemoryManager = this.MemoryManager; - cfg.ImageOperationsProvider = this.ImageOperationsProvider; + return new Configuration + { + ParallelOptions = this.ParallelOptions, + ImageFormatsManager = this.ImageFormatsManager, + MemoryManager = this.MemoryManager, + ImageOperationsProvider = this.ImageOperationsProvider, + ReadOrigin = this.ReadOrigin, #if !NETSTANDARD1_1 - cfg.FileSystem = this.FileSystem; + FileSystem = this.FileSystem #endif - - cfg.ReadOrigin = this.ReadOrigin; - - return cfg; - } - - /// - /// Registers a new format provider. - /// - /// The format to register as a known format. - public void AddImageFormat(IImageFormat format) - { - this.ImageFormatsManager.AddImageFormat(format); - } - - /// - /// For the specified file extensions type find the e . - /// - /// The extension to discover - /// The if found otherwise null - public IImageFormat FindFormatByFileExtension(string extension) - { - return this.ImageFormatsManager.FindFormatByFileExtension(extension); - } - - /// - /// For the specified mime type find the . - /// - /// The mime-type to discover - /// The if found; otherwise null - public IImageFormat FindFormatByMimeType(string mimeType) - { - return this.ImageFormatsManager.FindFormatByMimeType(mimeType); - } - - /// - /// Sets a specific image encoder as the encoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The encoder to use, - public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) - { - this.ImageFormatsManager.SetEncoder(imageFormat, encoder); - } - - /// - /// Sets a specific image decoder as the decoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The decoder to use, - public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) - { - this.ImageFormatsManager.SetDecoder(imageFormat, decoder); - } - - /// - /// Removes all the registered image format detectors. - /// - public void ClearImageFormatDetectors() - { - this.ImageFormatsManager.ClearImageFormatDetectors(); - } - - /// - /// Adds a new detector for detecting mime types. - /// - /// The detector to add - public void AddImageFormatDetector(IImageFormatDetector detector) - { - this.ImageFormatsManager.AddImageFormatDetector(detector); - } - - /// - /// For the specified mime type find the decoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageDecoder FindDecoder(IImageFormat format) - { - return this.ImageFormatsManager.FindDecoder(format); - } - - /// - /// For the specified mime type find the encoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageEncoder FindEncoder(IImageFormat format) - { - return this.ImageFormatsManager.FindEncoder(format); + }; } /// diff --git a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs index b091467bf5..956acc1578 100644 --- a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs +++ b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs @@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public void Configure(Configuration config) { - config.SetEncoder(ImageFormats.Bmp, new BmpEncoder()); - config.SetDecoder(ImageFormats.Bmp, new BmpDecoder()); - config.AddImageFormatDetector(new BmpImageFormatDetector()); + config.ImageFormatsManager.SetEncoder(ImageFormats.Bmp, new BmpEncoder()); + config.ImageFormatsManager.SetDecoder(ImageFormats.Bmp, new BmpDecoder()); + config.ImageFormatsManager.AddImageFormatDetector(new BmpImageFormatDetector()); } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs index 935ce8f4ad..35e168e278 100644 --- a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs @@ -36,6 +36,6 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is null. public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder) where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Bmp)); + => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Bmp)); } } diff --git a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs index 4c42a833c0..0bb62779eb 100644 --- a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs +++ b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs @@ -11,10 +11,10 @@ namespace SixLabors.ImageSharp.Formats.Gif /// public void Configure(Configuration config) { - config.SetEncoder(ImageFormats.Gif, new GifEncoder()); - config.SetDecoder(ImageFormats.Gif, new GifDecoder()); + config.ImageFormatsManager.SetEncoder(ImageFormats.Gif, new GifEncoder()); + config.ImageFormatsManager.SetDecoder(ImageFormats.Gif, new GifDecoder()); - config.AddImageFormatDetector(new GifImageFormatDetector()); + config.ImageFormatsManager.AddImageFormatDetector(new GifImageFormatDetector()); } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/ImageExtensions.cs b/src/ImageSharp/Formats/Gif/ImageExtensions.cs index 939eb456e1..78acadb4b1 100644 --- a/src/ImageSharp/Formats/Gif/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Gif/ImageExtensions.cs @@ -36,6 +36,6 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is null. public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder) where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Gif)); + => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Gif)); } } diff --git a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs index 9cd7b3a8bd..d3f95e40c0 100644 --- a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs @@ -36,6 +36,6 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is null. public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Jpeg)); + => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Jpeg)); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs b/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs index 1ab5093398..23cef59273 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs @@ -11,10 +11,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public void Configure(Configuration config) { - config.SetEncoder(ImageFormats.Jpeg, new JpegEncoder()); - config.SetDecoder(ImageFormats.Jpeg, new JpegDecoder()); + config.ImageFormatsManager.SetEncoder(ImageFormats.Jpeg, new JpegEncoder()); + config.ImageFormatsManager.SetDecoder(ImageFormats.Jpeg, new JpegDecoder()); - config.AddImageFormatDetector(new JpegImageFormatDetector()); + config.ImageFormatsManager.AddImageFormatDetector(new JpegImageFormatDetector()); } } } diff --git a/src/ImageSharp/Formats/Png/ImageExtensions.cs b/src/ImageSharp/Formats/Png/ImageExtensions.cs index 10970fc16a..f25d2bffe2 100644 --- a/src/ImageSharp/Formats/Png/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Png/ImageExtensions.cs @@ -35,6 +35,6 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is null. public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder) where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Png)); + => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Png)); } } diff --git a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs index ab6f31d49a..0036280a83 100644 --- a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs +++ b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs @@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.Formats.Png /// public void Configure(Configuration config) { - config.SetEncoder(ImageFormats.Png, new PngEncoder()); - config.SetDecoder(ImageFormats.Png, new PngDecoder()); - config.AddImageFormatDetector(new PngImageFormatDetector()); + config.ImageFormatsManager.SetEncoder(ImageFormats.Png, new PngEncoder()); + config.ImageFormatsManager.SetDecoder(ImageFormats.Png, new PngDecoder()); + config.ImageFormatsManager.AddImageFormatDetector(new PngImageFormatDetector()); } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs index 72492a494b..6b44c893bc 100644 --- a/src/ImageSharp/Image/Image.Decode.cs +++ b/src/ImageSharp/Image/Image.Decode.cs @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp long startPosition = stream.Position; stream.Read(buffer.Array, 0, maxHeaderSize); stream.Position = startPosition; - return config.FormatDetectors.Select(x => x.DetectFormat(buffer.Span)).LastOrDefault(x => x != null); + return config.ImageFormatsManager.FormatDetectors.Select(x => x.DetectFormat(buffer.Span)).LastOrDefault(x => x != null); } } @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp format = InternalDetectFormat(stream, config); if (format != null) { - return config.FindDecoder(format); + return config.ImageFormatsManager.FindDecoder(format); } return null; diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs index 4e294260aa..9061c334dc 100644 --- a/src/ImageSharp/Image/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -186,7 +186,7 @@ namespace SixLabors.ImageSharp var stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Image cannot be loaded. Available decoders:"); - foreach (KeyValuePair val in config.ImageDecoders) + foreach (KeyValuePair val in config.ImageFormatsManager.ImageDecoders) { stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); } diff --git a/src/ImageSharp/Image/ImageExtensions.cs b/src/ImageSharp/Image/ImageExtensions.cs index c4de1c2988..7d23d95d9c 100644 --- a/src/ImageSharp/Image/ImageExtensions.cs +++ b/src/ImageSharp/Image/ImageExtensions.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp Guard.NotNullOrEmpty(filePath, nameof(filePath)); string ext = Path.GetExtension(filePath).Trim('.'); - IImageFormat format = source.GetConfiguration().FindFormatByFileExtension(ext); + IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext); if (format == null) { var stringBuilder = new StringBuilder(); @@ -45,13 +45,13 @@ namespace SixLabors.ImageSharp throw new NotSupportedException(stringBuilder.ToString()); } - IImageEncoder encoder = source.GetConfiguration().FindEncoder(format); + IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format); if (encoder == null) { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:"); - foreach (KeyValuePair enc in source.GetConfiguration().ImageEncoders) + foreach (KeyValuePair enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders) { stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}"); } @@ -93,14 +93,14 @@ namespace SixLabors.ImageSharp where TPixel : struct, IPixel { Guard.NotNull(format, nameof(format)); - IImageEncoder encoder = source.GetConfiguration().FindEncoder(format); + IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format); if (encoder == null) { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:"); - foreach (KeyValuePair val in source.GetConfiguration().ImageEncoders) + foreach (KeyValuePair val in source.GetConfiguration().ImageFormatsManager.ImageEncoders) { stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); } diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index d38556fa9e..cf348569ce 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -34,14 +34,7 @@ namespace SixLabors.ImageSharp.Tests { Assert.IsType(this.DefaultConfiguration.FileSystem); Assert.IsType(this.ConfigurationEmpty.FileSystem); - } - - [Fact] - public void IfAutoloadWellknwonFormatesIsTrueAllFormateAreLoaded() - { - Assert.Equal(4, this.DefaultConfiguration.ImageEncoders.Count()); - Assert.Equal(4, this.DefaultConfiguration.ImageDecoders.Count()); - } + } /// /// Test that the default configuration is not null. @@ -80,80 +73,6 @@ namespace SixLabors.ImageSharp.Tests Assert.True(Configuration.Default.ParallelOptions.MaxDegreeOfParallelism == Environment.ProcessorCount); } - [Fact] - public void AddImageFormatDetectorNullthrows() - { - Assert.Throws(() => - { - this.DefaultConfiguration.AddImageFormatDetector(null); - }); - } - - [Fact] - public void RegisterNullMimeTypeEncoder() - { - Assert.Throws(() => - { - this.DefaultConfiguration.SetEncoder(null, new Mock().Object); - }); - Assert.Throws(() => - { - this.DefaultConfiguration.SetEncoder(ImageFormats.Bmp, null); - }); - Assert.Throws(() => - { - this.DefaultConfiguration.SetEncoder(null, null); - }); - } - - [Fact] - public void RegisterNullSetDecoder() - { - Assert.Throws(() => - { - this.DefaultConfiguration.SetDecoder(null, new Mock().Object); - }); - Assert.Throws(() => - { - this.DefaultConfiguration.SetDecoder(ImageFormats.Bmp, null); - }); - Assert.Throws(() => - { - this.DefaultConfiguration.SetDecoder(null, null); - }); - } - - [Fact] - public void RegisterMimeTypeEncoderReplacesLast() - { - IImageEncoder encoder1 = new Mock().Object; - this.ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); - IImageEncoder found = this.ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat); - Assert.Equal(encoder1, found); - - IImageEncoder encoder2 = new Mock().Object; - this.ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); - IImageEncoder found2 = this.ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat); - Assert.Equal(encoder2, found2); - Assert.NotEqual(found, found2); - } - - [Fact] - public void RegisterMimeTypeDecoderReplacesLast() - { - IImageDecoder decoder1 = new Mock().Object; - this.ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); - IImageDecoder found = this.ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat); - Assert.Equal(decoder1, found); - - IImageDecoder decoder2 = new Mock().Object; - this.ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); - IImageDecoder found2 = this.ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat); - Assert.Equal(decoder2, found2); - Assert.NotEqual(found, found2); - } - - [Fact] public void ConstructorCallConfigureOnFormatProvider() { diff --git a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs index f19fa1990c..1a2275062e 100644 --- a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs +++ b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests FileSystem = this.fileSystem.Object }; - this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector); + this.LocalConfiguration.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector); TestFormat.RegisterGlobalTestFormat(); this.Marker = Guid.NewGuid().ToByteArray(); diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index de18714e2b..97274e98b3 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -54,8 +54,8 @@ namespace SixLabors.ImageSharp.Tests { FileSystem = this.fileSystem.Object }; - this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector); - this.LocalConfiguration.SetDecoder(this.localImageFormatMock.Object, this.localDecoder.Object); + this.LocalConfiguration.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector); + this.LocalConfiguration.ImageFormatsManager.SetDecoder(this.localImageFormatMock.Object, this.localDecoder.Object); TestFormat.RegisterGlobalTestFormat(); this.Marker = Guid.NewGuid().ToByteArray(); diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs index 7f6e3b7dac..028313e631 100644 --- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -42,8 +42,8 @@ namespace SixLabors.ImageSharp.Tests { FileSystem = this.fileSystem.Object }; - config.AddImageFormatDetector(this.localMimeTypeDetector); - config.SetEncoder(this.localImageFormat.Object, this.encoder.Object); + config.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector); + config.ImageFormatsManager.SetEncoder(this.localImageFormat.Object, this.encoder.Object); this.Image = new Image(config, 1, 1); } diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 078b708dfd..445ace9812 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -116,9 +116,9 @@ namespace SixLabors.ImageSharp.Tests public void Configure(Configuration host) { - host.AddImageFormatDetector(new TestHeader(this)); - host.SetEncoder(this, new TestEncoder(this)); - host.SetDecoder(this, new TestDecoder(this)); + host.ImageFormatsManager.AddImageFormatDetector(new TestHeader(this)); + host.ImageFormatsManager.SetEncoder(this, new TestEncoder(this)); + host.ImageFormatsManager.SetDecoder(this, new TestDecoder(this)); } public struct DecodeOperation diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 089fed6b0a..fa9497a8f8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -21,20 +21,20 @@ namespace SixLabors.ImageSharp.Tests internal static IImageDecoder GetReferenceDecoder(string filePath) { IImageFormat format = GetImageFormat(filePath); - return Configuration.FindDecoder(format); + return Configuration.ImageFormatsManager.FindDecoder(format); } internal static IImageEncoder GetReferenceEncoder(string filePath) { IImageFormat format = GetImageFormat(filePath); - return Configuration.FindEncoder(format); + return Configuration.ImageFormatsManager.FindEncoder(format); } internal static IImageFormat GetImageFormat(string filePath) { string extension = Path.GetExtension(filePath).ToLower(); if (extension[0] == '.') extension = extension.Substring(1); - IImageFormat format = Configuration.FindFormatByFileExtension(extension); + IImageFormat format = Configuration.ImageFormatsManager.FindFormatByFileExtension(extension); return format; } @@ -45,9 +45,9 @@ namespace SixLabors.ImageSharp.Tests IImageEncoder encoder, IImageFormatDetector detector) { - cfg.SetDecoder(imageFormat, decoder); - cfg.SetEncoder(imageFormat, encoder); - cfg.AddImageFormatDetector(detector); + cfg.ImageFormatsManager.SetDecoder(imageFormat, decoder); + cfg.ImageFormatsManager.SetEncoder(imageFormat, encoder); + cfg.ImageFormatsManager.AddImageFormatDetector(detector); } private static Configuration CreateDefaultConfiguration() diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 6014e25334..f03f9db09a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -360,7 +360,7 @@ namespace SixLabors.ImageSharp.Tests IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); IImageFormat format = TestEnvironment.GetImageFormat(path); - IImageDecoder defaultDecoder = Configuration.Default.FindDecoder(format); + IImageDecoder defaultDecoder = Configuration.Default.ImageFormatsManager.FindDecoder(format); //if (referenceDecoder.GetType() == defaultDecoder.GetType()) //{ From 73a7f8c06910bd3118849e018aa3e47d73e7f5d4 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 3 Mar 2018 01:47:44 +0100 Subject: [PATCH 20/20] sanitize MCU processing code --- .../Components/Decoder/InputProcessor.cs | 8 + .../Components/Decoder/OrigJpegScanDecoder.cs | 205 ++++++++++-------- .../Jpeg/GolangPort/OrigJpegDecoderCore.cs | 9 + 3 files changed, 133 insertions(+), 89 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index fd6a7833a7..cb4b63cffd 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -380,5 +380,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder this.LastErrorCode = this.Bits.ReceiveExtendUnsafe(t, ref this, out x); return this.LastErrorCode; } + + /// + /// Reset the Huffman decoder. + /// + public void ResetHuffmanDecoder() + { + this.Bits = default(Bits); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs index 2e1372c564..d10def3ce7 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs @@ -94,6 +94,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// private int eobRun; + /// + /// The block counter + /// + private int blockCounter; + + /// + /// The MCU counter + /// + private int mcuCounter; + + /// + /// The expected RST marker value + /// + private byte expectedRst; + /// /// Initializes a default-constructed instance for reading data from -s stream. /// @@ -139,124 +154,136 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { decoder.InputProcessor.ResetErrorState(); - int blockCount = 0; - int mcu = 0; - byte expectedRst = OrigJpegConstants.Markers.RST0; + this.blockCounter = 0; + this.mcuCounter = 0; + this.expectedRst = OrigJpegConstants.Markers.RST0; for (int my = 0; my < decoder.MCUCountY; my++) { for (int mx = 0; mx < decoder.MCUCountX; mx++) { - for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++) - { - this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex; - OrigComponent component = decoder.Components[this.ComponentIndex]; + this.DecodeBlocksAtMcuIndex(decoder, mx, my); - this.hi = component.HorizontalSamplingFactor; - int vi = component.VerticalSamplingFactor; - - for (int j = 0; j < this.hi * vi; j++) - { - if (this.componentScanCount != 1) - { - this.bx = (this.hi * mx) + (j % this.hi); - this.by = (vi * my) + (j / this.hi); - } - else - { - int q = decoder.MCUCountX * this.hi; - this.bx = blockCount % q; - this.by = blockCount / q; - blockCount++; - if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight) - { - continue; - } - } + this.mcuCounter++; - // Find the block at (bx,by) in the component's buffer: - ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by); + // Handling restart intervals + // Useful info: https://stackoverflow.com/a/8751802 + if (decoder.IsAtRestartInterval(this.mcuCounter)) + { + this.ProcessRSTMarker(decoder); + this.Reset(decoder); + } + } + } + } - // Copy block to stack - this.data.Block = blockRefOnHeap; + private void DecodeBlocksAtMcuIndex(OrigJpegDecoderCore decoder, int mx, int my) + { + for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++) + { + this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex; + OrigComponent component = decoder.Components[this.ComponentIndex]; - if (!decoder.InputProcessor.ReachedEOF) - { - this.DecodeBlock(decoder, scanIndex); - } + this.hi = component.HorizontalSamplingFactor; + int vi = component.VerticalSamplingFactor; - // Store the result block: - blockRefOnHeap = this.data.Block; + for (int j = 0; j < this.hi * vi; j++) + { + if (this.componentScanCount != 1) + { + this.bx = (this.hi * mx) + (j % this.hi); + this.by = (vi * my) + (j / this.hi); + } + else + { + int q = decoder.MCUCountX * this.hi; + this.bx = this.blockCounter % q; + this.by = this.blockCounter / q; + this.blockCounter++; + if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight) + { + continue; } + } + + // Find the block at (bx,by) in the component's buffer: + ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by); - // for j + // Copy block to stack + this.data.Block = blockRefOnHeap; + + if (!decoder.InputProcessor.ReachedEOF) + { + this.DecodeBlock(decoder, scanIndex); } - // for i - mcu++; + // Store the result block: + blockRefOnHeap = this.data.Block; + } + } + } - if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < decoder.TotalMCUCount) + private void ProcessRSTMarker(OrigJpegDecoderCore decoder) + { + // Attempt to look for RST[0-7] markers to resynchronize from corrupt input. + if (!decoder.InputProcessor.ReachedEOF) + { + decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2); + if (decoder.InputProcessor.CheckEOFEnsureNoError()) + { + if (decoder.Temp[0] != 0xFF || decoder.Temp[1] != this.expectedRst) { - // Attempt to look for RST[0-7] markers to resynchronize from corrupt input. - if (!decoder.InputProcessor.ReachedEOF) + bool invalidRst = true; + + // Most jpeg's containing well-formed input will have a RST[0-7] marker following immediately + // but some, see Issue #481, contain padding bytes "0xFF" before the RST[0-7] marker. + // If we identify that case we attempt to read until we have bypassed the padded bytes. + // We then check again for our RST marker and throw if invalid. + // No other methods are attempted to resynchronize from corrupt input. + if (decoder.Temp[0] == 0xFF && decoder.Temp[1] == 0xFF) { - decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2); - if (decoder.InputProcessor.CheckEOFEnsureNoError()) + while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError()) { - if (decoder.Temp[0] != 0xFF || decoder.Temp[1] != expectedRst) - { - bool invalidRst = true; - - // Most jpeg's containing well-formed input will have a RST[0-7] marker following immediately - // but some, see Issue #481, contain padding bytes "0xFF" before the RST[0-7] marker. - // If we identify that case we attempt to read until we have bypassed the padded bytes. - // We then check again for our RST marker and throw if invalid. - // No other methods are attempted to resynchronize from corrupt input. - if (decoder.Temp[0] == 0xFF && decoder.Temp[1] == 0xFF) - { - while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError()) - { - decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1); - if (!decoder.InputProcessor.CheckEOFEnsureNoError()) - { - break; - } - } - - // Have we found a valid restart marker? - invalidRst = decoder.Temp[0] != expectedRst; - } - - if (invalidRst) - { - throw new ImageFormatException("Bad RST marker"); - } - } - - expectedRst++; - if (expectedRst == OrigJpegConstants.Markers.RST7 + 1) + decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1); + if (!decoder.InputProcessor.CheckEOFEnsureNoError()) { - expectedRst = OrigJpegConstants.Markers.RST0; + break; } } - } - // Reset the Huffman decoder. - decoder.InputProcessor.Bits = default(Bits); + // Have we found a valid restart marker? + invalidRst = decoder.Temp[0] != this.expectedRst; + } - // Reset the DC components, as per section F.2.1.3.1. - this.ResetDc(); + if (invalidRst) + { + throw new ImageFormatException("Bad RST marker"); + } + } - // Reset the progressive decoder state, as per section G.1.2.2. - this.eobRun = 0; + this.expectedRst++; + if (this.expectedRst == OrigJpegConstants.Markers.RST7 + 1) + { + this.expectedRst = OrigJpegConstants.Markers.RST0; } } - - // for mx } } - private void ResetDc() + private void Reset(OrigJpegDecoderCore decoder) + { + decoder.InputProcessor.ResetHuffmanDecoder(); + + this.ResetDcValues(); + + // Reset the progressive decoder state, as per section G.1.2.2. + this.eobRun = 0; + } + + /// + /// Reset the DC components, as per section F.2.1.3.1. + /// + private void ResetDcValues() { Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * OrigJpegDecoderCore.MaxComponents); } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index 58513fd297..6cc275d49d 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -411,6 +411,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort this.InitDerivedMetaDataProperties(); } + /// + /// Returns true if 'mcuCounter' is at restart interval + /// + public bool IsAtRestartInterval(int mcuCounter) + { + return this.RestartInterval > 0 && mcuCounter % this.RestartInterval == 0 + && mcuCounter < this.TotalMCUCount; + } + /// /// Assigns derived metadata properties to , eg. horizontal and vertical resolution if it has a JFIF header. ///