diff --git a/.editorconfig b/.editorconfig index 03036f8a53..33fd0577a8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -75,7 +75,7 @@ indent_style = tab [*.{cs,csx,cake,vb,vbx}] # Default Severity for all .NET Code Style rules below -dotnet_analyzer_diagnostic.severity = warning +dotnet_analyzer_diagnostic.category-style.severity = warning ########################################## # Language Rules diff --git a/Directory.Build.props b/Directory.Build.props index 3df93fcd40..d70fbc45ae 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -26,5 +26,5 @@ true - + diff --git a/ImageSharp.sln b/ImageSharp.sln index 5555764353..ef6a945f65 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -546,12 +546,12 @@ Global {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|x64.Build.0 = Debug|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|x86.ActiveCfg = Debug|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|x86.Build.0 = Debug|Any CPU - {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU - {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU - {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|x64.ActiveCfg = Debug-InnerLoop|Any CPU - {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|x64.Build.0 = Debug-InnerLoop|Any CPU - {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|x86.ActiveCfg = Debug-InnerLoop|Any CPU - {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|x86.Build.0 = Debug-InnerLoop|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|Any CPU.Build.0 = Debug|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|x64.ActiveCfg = Debug|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|x64.Build.0 = Debug|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|x86.ActiveCfg = Debug|Any CPU + {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|x86.Build.0 = Debug|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|Any CPU.ActiveCfg = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|Any CPU.Build.0 = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x64.ActiveCfg = Release|Any CPU @@ -570,12 +570,12 @@ Global {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x64.Build.0 = Debug|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x86.ActiveCfg = Debug|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x86.Build.0 = Debug|Any CPU - {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU - {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU - {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|x64.ActiveCfg = Debug-InnerLoop|Any CPU - {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|x64.Build.0 = Debug-InnerLoop|Any CPU - {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|x86.ActiveCfg = Debug-InnerLoop|Any CPU - {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|x86.Build.0 = Debug-InnerLoop|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|Any CPU.Build.0 = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|x64.ActiveCfg = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|x64.Build.0 = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|x86.ActiveCfg = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|x86.Build.0 = Debug|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Release|Any CPU.Build.0 = Release|Any CPU {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x64.ActiveCfg = Release|Any CPU diff --git a/shared-infrastructure b/shared-infrastructure index 1f7ee70281..9b94ebc4be 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 1f7ee702812f3a1713ab7f749c0faae0ef139ed7 +Subproject commit 9b94ebc4be9b7a8d7620c257e6ee485455973332 diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index ef457f7ceb..db65b84cca 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -24,24 +24,12 @@ namespace SixLabors.ImageSharp #endif #if !SUPPORTS_BITOPERATIONS - /// - /// Gets the counts the number of bits needed to hold an integer. - /// - private static ReadOnlySpan BitCountLut => new byte[] + private static ReadOnlySpan Log2DeBruijn => new byte[32] { - 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, + 00, 09, 01, 10, 13, 21, 02, 29, + 11, 14, 16, 18, 22, 25, 03, 30, + 08, 12, 20, 28, 15, 17, 24, 07, + 19, 27, 23, 06, 26, 05, 04, 31 }; #endif @@ -849,24 +837,47 @@ namespace SixLabors.ImageSharp #endif /// - /// Calculates how many minimum bits needed to store given value. + /// Calculates floored log of the specified value, base 2. + /// Note that by convention, input value 0 returns 0 since Log(0) is undefined. /// - /// Unsigned integer to store - /// Minimum number of bits needed to store given value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int MinimumBitsToStore16(uint number) + /// The value. + public static int Log2(uint value) { -#if !SUPPORTS_BITOPERATIONS - if (number < 0x100) - { - return BitCountLut[(int)number]; - } - - return 8 + BitCountLut[(int)number >> 8]; +#if SUPPORTS_BITOPERATIONS + return BitOperations.Log2(value); #else - const int bitInUnsignedInteger = sizeof(uint) * 8; - return bitInUnsignedInteger - BitOperations.LeadingZeroCount(number); + return Log2SoftwareFallback(value); #endif } + +#if !SUPPORTS_BITOPERATIONS + /// + /// Calculates floored log of the specified value, base 2. + /// Note that by convention, input value 0 returns 0 since Log(0) is undefined. + /// Bit hacking with deBruijn sequence, extremely fast yet does not use any intrinsics so will work on every platform/runtime. + /// + /// + /// Description of this bit hacking can be found here: + /// https://cstheory.stackexchange.com/questions/19524/using-the-de-bruijn-sequence-to-find-the-lceil-log-2-v-rceil-of-an-integer + /// + /// The value. + private static int Log2SoftwareFallback(uint value) + { + // No AggressiveInlining due to large method size + // Has conventional contract 0->0 (Log(0) is undefined) by default, no need for if checking + + // Fill trailing zeros with ones, eg 00010010 becomes 00011111 + value |= value >> 01; + value |= value >> 02; + value |= value >> 04; + value |= value >> 08; + value |= value >> 16; + + // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check + return Unsafe.AddByteOffset( + ref MemoryMarshal.GetReference(Log2DeBruijn), + (IntPtr)(int)((value * 0x07C4ACDDu) >> 27)); // uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here + } +#endif } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs index ca352397b8..860a9c3236 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs @@ -360,7 +360,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder b = value - 1; } - int bt = Numerics.MinimumBitsToStore16((uint)a); + int bt = GetHuffmanEncodingLength((uint)a); this.EmitHuff(index, (runLength << 4) | bt); if (bt > 0) @@ -388,5 +388,40 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder this.target.Write(this.emitBuffer, 0, this.emitLen); } } + + /// + /// Calculates how many minimum bits needed to store given value for Huffman jpeg encoding. + /// + /// + /// This method returns 0 for input value 0. This is done specificaly for huffman encoding + /// + /// The value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetHuffmanEncodingLength(uint value) + { + DebugGuard.IsTrue(value <= (1 << 16), "Huffman encoder is supposed to encode a value of 16bit size max"); +#if SUPPORTS_BITOPERATIONS + // This should have been implemented as (BitOperations.Log2(value) + 1) as in non-intrinsic implementation + // But internal log2 is implementated like this: (31 - (int)Lzcnt.LeadingZeroCount(value)) + + // BitOperations.Log2 implementation also checks if input value is zero for the convention 0->0 + // Lzcnt would return 32 for input value of 0 - no need to check that with branching + // Fallback code if Lzcnt is not supported still use if-check + // But most modern CPUs support this instruction so this should not be a problem + return 32 - System.Numerics.BitOperations.LeadingZeroCount(value); +#else + // Ideally: + // if 0 - return 0 in this case + // else - return log2(value) + 1 + // + // Hack based on input value constaint: + // We know that input values are guaranteed to be maximum 16 bit large for huffman encoding + // We can safely shift input value for one bit -> log2(value << 1) + // Because of the 16 bit value constraint it won't overflow + // With that input value change we no longer need to add 1 before returning + // And this eliminates need to check if input value is zero - it is a standard convention which Log2SoftwareFallback adheres to + return Numerics.Log2(value << 1); +#endif + } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs index 926e7d5a4a..9566ee862a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs @@ -175,7 +175,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder // left 8x8 column conversions for (int j = 0; j < 4; j += 2) { - rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * (i * 4 + j))).AsUInt32(), extractToLanesMask).AsByte(); + rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * ((i * 4) + j))).AsUInt32(), extractToLanesMask).AsByte(); rgb = Avx2.Shuffle(rgb, extractRgbMask); @@ -200,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder // right 8x8 column conversions for (int j = 1; j < 4; j += 2) { - rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * (i * 4 + j))).AsUInt32(), extractToLanesMask).AsByte(); + rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * ((i * 4) + j))).AsUInt32(), extractToLanesMask).AsByte(); rgb = Avx2.Shuffle(rgb, extractRgbMask); diff --git a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs index f31d07efca..0f569b5da1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components private static readonly Vector256 C_V_n1_8477 = Vector256.Create(-1.847759065f); private static readonly Vector256 C_V_0_7653 = Vector256.Create(0.765366865f); - private static Vector256 C_V_InvSqrt2 = Vector256.Create(0.707107f); + private static readonly Vector256 C_V_InvSqrt2 = Vector256.Create(0.707107f); #endif #pragma warning restore SA1310 // FieldNamesMustNotContainUnderscore private static readonly Vector4 InvSqrt2 = new Vector4(0.707107f); diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 6020e6196c..135048aa4e 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -28,44 +28,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// private const int QuantizationTableCount = 2; - /// - /// Gets the unscaled quantization tables in zig-zag order. Each - /// encoder copies and scales the tables according to its quality parameter. - /// The values are derived from section K.1 after converting from natural to - /// zig-zag order. - /// - // The C# compiler emits this as a compile-time constant embedded in the PE file. - // This is effectively compiled down to: return new ReadOnlySpan(&data, length) - // More details can be found: https://github.com/dotnet/roslyn/pull/24621 - private static ReadOnlySpan UnscaledQuant_Luminance => new byte[] - { - // Luminance. - 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, - 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, - 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, - 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, - 100, 120, 92, 101, 103, 99, - }; - - /// - /// Gets the unscaled quantization tables in zig-zag order. Each - /// encoder copies and scales the tables according to its quality parameter. - /// The values are derived from section K.1 after converting from natural to - /// zig-zag order. - /// - // The C# compiler emits this as a compile-time constant embedded in the PE file. - // This is effectively compiled down to: return new ReadOnlySpan(&data, length) - // More details can be found: https://github.com/dotnet/roslyn/pull/24621 - private static ReadOnlySpan UnscaledQuant_Chrominance => new byte[] - { - // Chrominance. - 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - }; - /// /// A scratch buffer to reduce allocations. /// @@ -102,6 +64,44 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.colorType = options.ColorType; } + /// + /// Gets the unscaled quantization tables in zig-zag order. Each + /// encoder copies and scales the tables according to its quality parameter. + /// The values are derived from section K.1 after converting from natural to + /// zig-zag order. + /// + // The C# compiler emits this as a compile-time constant embedded in the PE file. + // This is effectively compiled down to: return new ReadOnlySpan(&data, length) + // More details can be found: https://github.com/dotnet/roslyn/pull/24621 + private static ReadOnlySpan UnscaledQuant_Luminance => new byte[] + { + // Luminance. + 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, + 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, + 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, + 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, + 100, 120, 92, 101, 103, 99, + }; + + /// + /// Gets the unscaled quantization tables in zig-zag order. Each + /// encoder copies and scales the tables according to its quality parameter. + /// The values are derived from section K.1 after converting from natural to + /// zig-zag order. + /// + // The C# compiler emits this as a compile-time constant embedded in the PE file. + // This is effectively compiled down to: return new ReadOnlySpan(&data, length) + // More details can be found: https://github.com/dotnet/roslyn/pull/24621 + private static ReadOnlySpan UnscaledQuant_Chrominance => new byte[] + { + // Chrominance. + 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + }; + /// /// Encode writes the image to the jpeg baseline format with the given options. /// @@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } else { - switch (subsample) + switch (this.subsample) { case JpegSubsample.Ratio444: scanEncoder.Encode444(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken); diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs index 3e9b7f4e63..30da537eb2 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs @@ -213,7 +213,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors this.compressedDataBuffer = this.Allocator.Allocate(maxNeededBytes); } - /// Writes a image compressed with CCITT T4 to the stream. + /// + /// Writes a image compressed with CCITT T4 to the stream. + /// /// The pixels as 8-bit gray array. /// The strip height. public override void CompressStrip(Span pixelsAsGray, int height) diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 6fe412b925..b545451412 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -40,41 +40,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants /// public const int RowsPerStripInfinity = 2147483647; - /// - /// Size (in bytes) of the TIFF file header. - /// - public const int SizeOfTiffHeader = 8; - - /// - /// Size (in bytes) of each individual TIFF IFD entry - /// - public const int SizeOfIfdEntry = 12; - - /// - /// Size (in bytes) of the Short and SShort data types - /// - public const int SizeOfShort = 2; - - /// - /// Size (in bytes) of the Long and SLong data types - /// - public const int SizeOfLong = 4; - /// /// Size (in bytes) of the Rational and SRational data types /// public const int SizeOfRational = 8; - /// - /// Size (in bytes) of the Float data type - /// - public const int SizeOfFloat = 4; - - /// - /// Size (in bytes) of the Double data type - /// - public const int SizeOfDouble = 8; - /// /// The default strip size is 8k. /// @@ -83,42 +53,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants /// /// The bits per sample for 1 bit bicolor images. /// - public static readonly ushort[] BitsPerSample1Bit = { 1 }; + public static readonly TiffBitsPerSample BitsPerSample1Bit = new TiffBitsPerSample(1, 0, 0); /// /// The bits per sample for images with a 4 color palette. /// - public static readonly ushort[] BitsPerSample4Bit = { 4 }; + public static readonly TiffBitsPerSample BitsPerSample4Bit = new TiffBitsPerSample(4, 0, 0); /// /// The bits per sample for 8 bit images. /// - public static readonly ushort[] BitsPerSample8Bit = { 8 }; - - /// - /// The bits per sample for color images with 2 bits for each color channel. - /// - public static readonly ushort[] BitsPerSampleRgb2Bit = { 2, 2, 2 }; - - /// - /// The bits per sample for color images with 4 bits for each color channel. - /// - public static readonly ushort[] BitsPerSampleRgb4Bit = { 4, 4, 4 }; + public static readonly TiffBitsPerSample BitsPerSample8Bit = new TiffBitsPerSample(8, 0, 0); /// /// The bits per sample for color images with 8 bits for each color channel. /// - public static readonly ushort[] BitsPerSampleRgb8Bit = { 8, 8, 8 }; - - /// - /// The bits per sample for color images with 10 bits for each color channel. - /// - public static readonly ushort[] BitsPerSampleRgb10Bit = { 10, 10, 10 }; - - /// - /// The bits per sample for color images with 14 bits for each color channel. - /// - public static readonly ushort[] BitsPerSampleRgb14Bit = { 14, 14, 14 }; + public static readonly TiffBitsPerSample BitsPerSampleRgb8Bit = new TiffBitsPerSample(8, 8, 8); /// /// The list of mimetypes that equate to a tiff. diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs index 83cef8e758..a4e5e45dfb 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs @@ -19,9 +19,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation private readonly float factor; - public BlackIsZeroTiffColor(ushort[] bitsPerSample) + public BlackIsZeroTiffColor(TiffBitsPerSample bitsPerSample) { - this.bitsPerSample0 = bitsPerSample[0]; + this.bitsPerSample0 = bitsPerSample.Channel0; this.factor = (1 << this.bitsPerSample0) - 1.0f; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs index 7ed25f8221..796227953e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs @@ -21,9 +21,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// The number of bits per sample for each pixel. /// The RGB color lookup table to use for decoding the image. - public PaletteTiffColor(ushort[] bitsPerSample, ushort[] colorMap) + public PaletteTiffColor(TiffBitsPerSample bitsPerSample, ushort[] colorMap) { - this.bitsPerSample0 = bitsPerSample[0]; + this.bitsPerSample0 = bitsPerSample.Channel0; int colorCount = 1 << this.bitsPerSample0; this.palette = GeneratePalette(colorMap, colorCount); } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs index b40158fcee..8dda0cf38f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs @@ -26,11 +26,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation private readonly ushort bitsPerSampleB; - public RgbPlanarTiffColor(ushort[] bitsPerSample) + public RgbPlanarTiffColor(TiffBitsPerSample bitsPerSample) { - this.bitsPerSampleR = bitsPerSample[0]; - this.bitsPerSampleG = bitsPerSample[1]; - this.bitsPerSampleB = bitsPerSample[2]; + this.bitsPerSampleR = bitsPerSample.Channel0; + this.bitsPerSampleG = bitsPerSample.Channel1; + this.bitsPerSampleB = bitsPerSample.Channel2; this.rFactor = (1 << this.bitsPerSampleR) - 1.0f; this.gFactor = (1 << this.bitsPerSampleG) - 1.0f; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs index 816ba67b76..259bb8efa7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs @@ -27,11 +27,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation private readonly ushort bitsPerSampleB; - public RgbTiffColor(ushort[] bitsPerSample) + public RgbTiffColor(TiffBitsPerSample bitsPerSample) { - this.bitsPerSampleR = bitsPerSample[0]; - this.bitsPerSampleG = bitsPerSample[1]; - this.bitsPerSampleB = bitsPerSample[2]; + this.bitsPerSampleR = bitsPerSample.Channel0; + this.bitsPerSampleG = bitsPerSample.Channel1; + this.bitsPerSampleB = bitsPerSample.Channel2; this.rFactor = (1 << this.bitsPerSampleR) - 1.0f; this.gFactor = (1 << this.bitsPerSampleG) - 1.0f; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs index 5555eb537c..36d2ab746e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs @@ -8,107 +8,125 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation internal static class TiffColorDecoderFactory where TPixel : unmanaged, IPixel { - public static TiffBaseColorDecoder Create(TiffColorType colorType, ushort[] bitsPerSample, ushort[] colorMap) + public static TiffBaseColorDecoder Create(TiffColorType colorType, TiffBitsPerSample bitsPerSample, ushort[] colorMap) { switch (colorType) { case TiffColorType.WhiteIsZero: - DebugGuard.IsTrue(bitsPerSample.Length == 1, "bitsPerSample"); + DebugGuard.IsTrue(bitsPerSample.Channels == 1, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new WhiteIsZeroTiffColor(bitsPerSample); case TiffColorType.WhiteIsZero1: - DebugGuard.IsTrue(bitsPerSample.Length == 1 && bitsPerSample[0] == 1, "bitsPerSample"); + DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 1, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new WhiteIsZero1TiffColor(); case TiffColorType.WhiteIsZero4: - DebugGuard.IsTrue(bitsPerSample.Length == 1 && bitsPerSample[0] == 4, "bitsPerSample"); + DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 4, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new WhiteIsZero4TiffColor(); case TiffColorType.WhiteIsZero8: - DebugGuard.IsTrue(bitsPerSample.Length == 1 && bitsPerSample[0] == 8, "bitsPerSample"); + DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 8, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new WhiteIsZero8TiffColor(); case TiffColorType.BlackIsZero: - DebugGuard.IsTrue(bitsPerSample.Length == 1, "bitsPerSample"); + DebugGuard.IsTrue(bitsPerSample.Channels == 1, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new BlackIsZeroTiffColor(bitsPerSample); case TiffColorType.BlackIsZero1: - DebugGuard.IsTrue(bitsPerSample.Length == 1 && bitsPerSample[0] == 1, "bitsPerSample"); + DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 1, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new BlackIsZero1TiffColor(); case TiffColorType.BlackIsZero4: - DebugGuard.IsTrue(bitsPerSample.Length == 1 && bitsPerSample[0] == 4, "bitsPerSample"); + DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 4, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new BlackIsZero4TiffColor(); case TiffColorType.BlackIsZero8: - DebugGuard.IsTrue(bitsPerSample.Length == 1 && bitsPerSample[0] == 8, "bitsPerSample"); + DebugGuard.IsTrue(bitsPerSample.Channels == 1 && bitsPerSample.Channel0 == 8, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new BlackIsZero8TiffColor(); case TiffColorType.Rgb: - DebugGuard.NotNull(bitsPerSample, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new RgbTiffColor(bitsPerSample); case TiffColorType.Rgb222: DebugGuard.IsTrue( - bitsPerSample.Length == 3 - && bitsPerSample[2] == 2 - && bitsPerSample[1] == 2 - && bitsPerSample[0] == 2, + bitsPerSample.Channels == 3 + && bitsPerSample.Channel2 == 2 + && bitsPerSample.Channel1 == 2 + && bitsPerSample.Channel0 == 2, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new RgbTiffColor(bitsPerSample); case TiffColorType.Rgb444: DebugGuard.IsTrue( - bitsPerSample.Length == 3 - && bitsPerSample[2] == 4 - && bitsPerSample[1] == 4 - && bitsPerSample[0] == 4, + bitsPerSample.Channels == 3 + && bitsPerSample.Channel2 == 4 + && bitsPerSample.Channel1 == 4 + && bitsPerSample.Channel0 == 4, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new Rgb444TiffColor(); case TiffColorType.Rgb888: DebugGuard.IsTrue( - bitsPerSample.Length == 3 - && bitsPerSample[2] == 8 - && bitsPerSample[1] == 8 - && bitsPerSample[0] == 8, + bitsPerSample.Channels == 3 + && bitsPerSample.Channel2 == 8 + && bitsPerSample.Channel1 == 8 + && bitsPerSample.Channel0 == 8, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new Rgb888TiffColor(); case TiffColorType.Rgb101010: DebugGuard.IsTrue( - bitsPerSample.Length == 3 - && bitsPerSample[2] == 10 - && bitsPerSample[1] == 10 - && bitsPerSample[0] == 10, + bitsPerSample.Channels == 3 + && bitsPerSample.Channel2 == 10 + && bitsPerSample.Channel1 == 10 + && bitsPerSample.Channel0 == 10, + "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new RgbTiffColor(bitsPerSample); + + case TiffColorType.Rgb121212: + DebugGuard.IsTrue( + bitsPerSample.Channels == 3 + && bitsPerSample.Channel2 == 12 + && bitsPerSample.Channel1 == 12 + && bitsPerSample.Channel0 == 12, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new RgbTiffColor(bitsPerSample); case TiffColorType.Rgb141414: DebugGuard.IsTrue( - bitsPerSample.Length == 3 - && bitsPerSample[2] == 14 - && bitsPerSample[1] == 14 - && bitsPerSample[0] == 14, + bitsPerSample.Channels == 3 + && bitsPerSample.Channel2 == 14 + && bitsPerSample.Channel1 == 14 + && bitsPerSample.Channel0 == 14, + "bitsPerSample"); + DebugGuard.IsTrue(colorMap == null, "colorMap"); + return new RgbTiffColor(bitsPerSample); + + case TiffColorType.Rgb161616: + DebugGuard.IsTrue( + bitsPerSample.Channels == 3 + && bitsPerSample.Channel2 == 16 + && bitsPerSample.Channel1 == 16 + && bitsPerSample.Channel0 == 16, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new RgbTiffColor(bitsPerSample); case TiffColorType.PaletteColor: - DebugGuard.NotNull(bitsPerSample, "bitsPerSample"); DebugGuard.NotNull(colorMap, "colorMap"); return new PaletteTiffColor(bitsPerSample, colorMap); @@ -117,12 +135,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation } } - public static RgbPlanarTiffColor CreatePlanar(TiffColorType colorType, ushort[] bitsPerSample, ushort[] colorMap) + public static RgbPlanarTiffColor CreatePlanar(TiffColorType colorType, TiffBitsPerSample bitsPerSample, ushort[] colorMap) { switch (colorType) { case TiffColorType.RgbPlanar: - DebugGuard.NotNull(bitsPerSample, "bitsPerSample"); DebugGuard.IsTrue(colorMap == null, "colorMap"); return new RgbPlanarTiffColor(bitsPerSample); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs index 22d8199533..517926c239 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs @@ -78,11 +78,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// Rgb101010, + /// + /// RGB color image with 12 bits for each channel. + /// + Rgb121212, + /// /// RGB color image with 14 bits for each channel. /// Rgb141414, + /// + /// RGB color image with 16 bits for each channel. + /// + Rgb161616, + /// /// RGB Full Color. Planar configuration of data. /// diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs index 697fe2f073..04b6f98e50 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs @@ -19,9 +19,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation private readonly float factor; - public WhiteIsZeroTiffColor(ushort[] bitsPerSample) + public WhiteIsZeroTiffColor(TiffBitsPerSample bitsPerSample) { - this.bitsPerSample0 = bitsPerSample[0]; + this.bitsPerSample0 = bitsPerSample.Channel0; this.factor = (float)Math.Pow(2, this.bitsPerSample0) - 1.0f; } diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs index ab9f3cbec0..73f3f4b77e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs @@ -30,6 +30,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Bit8 = 8, + /// + /// 10 bits per pixel, for gray images. + /// + /// Note: The TiffEncoder does not yet support 10 bits per pixel and will default to 24 bits per pixel instead. + /// + Bit10 = 10, + /// /// 12 bits per pixel. 4 bit for each color channel. /// @@ -37,6 +44,20 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Bit12 = 12, + /// + /// 14 bits per pixel, for gray images. + /// + /// Note: The TiffEncoder does not yet support 14 bits per pixel images and will default to 24 bits per pixel instead. + /// + Bit14 = 14, + + /// + /// 16 bits per pixel, for gray images. + /// + /// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 24 bits per pixel instead. + /// + Bit16 = 16, + /// /// 24 bits per pixel. One byte for each color channel. /// @@ -49,11 +70,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// Bit30 = 30, + /// + /// 36 bits per pixel. 12 bit for each color channel. + /// + /// Note: The TiffEncoder does not yet support 12 bits per color channel and will default to 24 bits per pixel instead. + /// + Bit36 = 36, + /// /// 42 bits per pixel. 14 bit for each color channel. /// /// Note: The TiffEncoder does not yet support 14 bits per color channel and will default to 24 bits per pixel instead. /// Bit42 = 42, + + /// + /// 48 bits per pixel. 16 bit for each color channel. + /// + /// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 24 bits per pixel instead. + /// + Bit48 = 48, } } diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs index 088ef5d6f8..8fd26ac13d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs @@ -1,56 +1,138 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; + namespace SixLabors.ImageSharp.Formats.Tiff { /// /// The number of bits per component. /// - public enum TiffBitsPerSample + public readonly struct TiffBitsPerSample : IEquatable { /// - /// The Bits per samples is not known. + /// The bits for the channel 0. /// - Unknown = 0, + public readonly ushort Channel0; /// - /// One bit per sample for bicolor images. + /// The bits for the channel 1. /// - Bit1 = 1, + public readonly ushort Channel1; /// - /// Four bits per sample for grayscale images with 16 different levels of gray or paletted images with a palette of 16 colors. + /// The bits for the channel 2. /// - Bit4 = 4, + public readonly ushort Channel2; /// - /// Eight bits per sample for grayscale images with 256 different levels of gray or paletted images with a palette of 256 colors. + /// The number of channels. /// - Bit8 = 8, + public readonly byte Channels; /// - /// Six bits per sample, each channel has 2 bits. + /// Initializes a new instance of the struct. /// - Bit6 = 6, + /// The bits for the channel 0. + /// The bits for the channel 1. + /// The bits for the channel 2. + public TiffBitsPerSample(ushort channel0, ushort channel1, ushort channel2) + { + this.Channel0 = (ushort)Numerics.Clamp(channel0, 0, 32); + this.Channel1 = (ushort)Numerics.Clamp(channel1, 0, 32); + this.Channel2 = (ushort)Numerics.Clamp(channel2, 0, 32); - /// - /// Twelve bits per sample, each channel has 4 bits. - /// - Bit12 = 12, + this.Channels = 0; + this.Channels += (byte)(this.Channel0 != 0 ? 1 : 0); + this.Channels += (byte)(this.Channel1 != 0 ? 1 : 0); + this.Channels += (byte)(this.Channel2 != 0 ? 1 : 0); + } /// - /// 24 bits per sample, each color channel has 8 Bits. + /// Tries to parse a ushort array and convert it into a TiffBitsPerSample struct. /// - Bit24 = 24, + /// The value to parse. + /// The tiff bits per sample. + /// True, if the value could be parsed. + public static bool TryParse(ushort[] value, out TiffBitsPerSample sample) + { + if (value is null || value.Length == 0) + { + sample = default; + return false; + } + + ushort c2; + ushort c1; + ushort c0; + switch (value.Length) + { + case 3: + c2 = value[2]; + c1 = value[1]; + c0 = value[0]; + break; + case 2: + c2 = 0; + c1 = value[1]; + c0 = value[0]; + break; + default: + c2 = 0; + c1 = 0; + c0 = value[0]; + break; + } + + sample = new TiffBitsPerSample(c0, c1, c2); + return true; + } + + /// + public override bool Equals(object obj) + => obj is TiffBitsPerSample sample && this.Equals(sample); + + /// + public bool Equals(TiffBitsPerSample other) + => this.Channel0 == other.Channel0 + && this.Channel1 == other.Channel1 + && this.Channel2 == other.Channel2; + + /// + public override int GetHashCode() + => HashCode.Combine(this.Channel0, this.Channel1, this.Channel2); /// - /// Thirty bits per sample, each channel has 10 bits. + /// Converts the bits per sample struct to an ushort array. /// - Bit30 = 30, + /// Bits per sample as ushort array. + public ushort[] ToArray() + { + if (this.Channel1 == 0) + { + return new[] { this.Channel0 }; + } + + if (this.Channel2 == 0) + { + return new[] { this.Channel0, this.Channel1 }; + } + + return new[] { this.Channel0, this.Channel1, this.Channel2 }; + } /// - /// Forty two bits per sample, each channel has 14 bits. + /// Gets the bits per pixel for the given bits per sample. /// - Bit42 = 42, + /// Bits per pixel. + public TiffBitsPerPixel BitsPerPixel() + { + int bitsPerPixel = this.Channel0 + this.Channel1 + this.Channel2; + return (TiffBitsPerPixel)bitsPerPixel; + } + + /// + public override string ToString() + => $"TiffBitsPerSample({this.Channel0}, {this.Channel1}, {this.Channel2})"; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs deleted file mode 100644 index ca0f0befcc..0000000000 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.ImageSharp.Formats.Tiff.Constants; - -namespace SixLabors.ImageSharp.Formats.Tiff -{ - internal static class TiffBitsPerSampleExtensions - { - /// - /// Gets the bits per channel array for a given BitsPerSample value, e,g, for RGB888: [8, 8, 8] - /// - /// The tiff bits per sample. - /// Bits per sample array. - public static ushort[] Bits(this TiffBitsPerSample tiffBitsPerSample) - { - switch (tiffBitsPerSample) - { - case TiffBitsPerSample.Bit1: - return TiffConstants.BitsPerSample1Bit; - case TiffBitsPerSample.Bit4: - return TiffConstants.BitsPerSample4Bit; - case TiffBitsPerSample.Bit6: - return TiffConstants.BitsPerSampleRgb2Bit; - case TiffBitsPerSample.Bit8: - return TiffConstants.BitsPerSample8Bit; - case TiffBitsPerSample.Bit12: - return TiffConstants.BitsPerSampleRgb4Bit; - case TiffBitsPerSample.Bit24: - return TiffConstants.BitsPerSampleRgb8Bit; - case TiffBitsPerSample.Bit30: - return TiffConstants.BitsPerSampleRgb10Bit; - case TiffBitsPerSample.Bit42: - return TiffConstants.BitsPerSampleRgb14Bit; - - default: - return Array.Empty(); - } - } - - /// - /// Maps an array of bits per sample to a concrete enum value. - /// - /// The bits per sample array. - /// TiffBitsPerSample enum value. - public static TiffBitsPerSample GetBitsPerSample(this ushort[] bitsPerSample) - { - switch (bitsPerSample.Length) - { - case 3: - if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb14Bit[2] && - bitsPerSample[1] == TiffConstants.BitsPerSampleRgb14Bit[1] && - bitsPerSample[0] == TiffConstants.BitsPerSampleRgb14Bit[0]) - { - return TiffBitsPerSample.Bit42; - } - - if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb10Bit[2] && - bitsPerSample[1] == TiffConstants.BitsPerSampleRgb10Bit[1] && - bitsPerSample[0] == TiffConstants.BitsPerSampleRgb10Bit[0]) - { - return TiffBitsPerSample.Bit30; - } - - if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb8Bit[2] && - bitsPerSample[1] == TiffConstants.BitsPerSampleRgb8Bit[1] && - bitsPerSample[0] == TiffConstants.BitsPerSampleRgb8Bit[0]) - { - return TiffBitsPerSample.Bit24; - } - - if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb4Bit[2] && - bitsPerSample[1] == TiffConstants.BitsPerSampleRgb4Bit[1] && - bitsPerSample[0] == TiffConstants.BitsPerSampleRgb4Bit[0]) - { - return TiffBitsPerSample.Bit12; - } - - if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb2Bit[2] && - bitsPerSample[1] == TiffConstants.BitsPerSampleRgb2Bit[1] && - bitsPerSample[0] == TiffConstants.BitsPerSampleRgb2Bit[0]) - { - return TiffBitsPerSample.Bit6; - } - - break; - - case 1: - if (bitsPerSample[0] == TiffConstants.BitsPerSample1Bit[0]) - { - return TiffBitsPerSample.Bit1; - } - - if (bitsPerSample[0] == TiffConstants.BitsPerSample4Bit[0]) - { - return TiffBitsPerSample.Bit4; - } - - if (bitsPerSample[0] == TiffConstants.BitsPerSample8Bit[0]) - { - return TiffBitsPerSample.Bit8; - } - - break; - } - - return TiffBitsPerSample.Unknown; - } - } -} diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index fadb4f7c2e..5ce696118d 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } /// - /// Gets or sets the number of bits per component of the pixel format used to decode the image. + /// Gets or sets the bits per sample. /// public TiffBitsPerSample BitsPerSample { get; set; } @@ -198,7 +198,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The size (in bytes) of the required pixel buffer. private int CalculateStripBufferSize(int width, int height, int plane = -1) { - int bitsPerPixel; + DebugGuard.MustBeLessThanOrEqualTo(plane, 3, nameof(plane)); + + int bitsPerPixel = 0; if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky) { @@ -207,7 +209,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff } else { - bitsPerPixel = this.BitsPerSample.Bits()[plane]; + switch (plane) + { + case 0: + bitsPerPixel = this.BitsPerSample.Channel0; + break; + case 1: + bitsPerPixel = this.BitsPerSample.Channel1; + break; + case 2: + bitsPerPixel = this.BitsPerSample.Channel2; + break; + default: + TiffThrowHelper.ThrowNotSupported("More then 3 color channels are not supported"); + break; + } } int bytesPerRow = ((width * bitsPerPixel) + 7) / 8; @@ -225,7 +241,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, Number[] stripOffsets, Number[] stripByteCounts) where TPixel : unmanaged, IPixel { - int stripsPerPixel = this.BitsPerSample.Bits().Length; + int stripsPerPixel = this.BitsPerSample.Channels; int stripsPerPlane = stripOffsets.Length / stripsPerPixel; int bitsPerPixel = this.BitsPerPixel; @@ -243,7 +259,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); - RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample.Bits(), this.ColorMap); + RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); for (int i = 0; i < stripsPerPlane; i++) { @@ -286,7 +302,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff using TiffBaseDecompressor decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, frame.Width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); - TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample.Bits(), this.ColorMap); + TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap); for (int stripIndex = 0; stripIndex < stripOffsets.Length; stripIndex++) { diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index eeac6a33c2..288f01cd1c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; using System.Linq; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Formats.Tiff.Constants; @@ -69,7 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff options.Predictor = frameMetadata.Predictor ?? TiffPredictor.None; options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffPhotometricInterpretation.Rgb; options.BitsPerPixel = frameMetadata.BitsPerPixel != null ? (int)frameMetadata.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24; - options.BitsPerSample = GetBitsPerSample(frameMetadata.BitsPerPixel); + options.BitsPerSample = frameMetadata.BitsPerSample ?? new TiffBitsPerSample(0, 0, 0); options.ParseColorType(exifProfile); options.ParseCompression(frameMetadata.Compression, exifProfile); @@ -99,26 +98,32 @@ namespace SixLabors.ImageSharp.Formats.Tiff { case TiffPhotometricInterpretation.WhiteIsZero: { - if (options.BitsPerSample.Bits().Length != 1) + if (options.BitsPerSample.Channels != 1) { TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } - switch (options.BitsPerSample) + ushort bitsPerChannel = options.BitsPerSample.Channel0; + if (bitsPerChannel > 16) { - case TiffBitsPerSample.Bit8: + TiffThrowHelper.ThrowNotSupported("Bits per sample is not supported."); + } + + switch (bitsPerChannel) + { + case 8: { options.ColorType = TiffColorType.WhiteIsZero8; break; } - case TiffBitsPerSample.Bit4: + case 4: { options.ColorType = TiffColorType.WhiteIsZero4; break; } - case TiffBitsPerSample.Bit1: + case 1: { options.ColorType = TiffColorType.WhiteIsZero1; break; @@ -136,26 +141,32 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffPhotometricInterpretation.BlackIsZero: { - if (options.BitsPerSample.Bits().Length != 1) + if (options.BitsPerSample.Channels != 1) { TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } - switch (options.BitsPerSample) + ushort bitsPerChannel = options.BitsPerSample.Channel0; + if (bitsPerChannel > 16) + { + TiffThrowHelper.ThrowNotSupported("Bits per sample is not supported."); + } + + switch (bitsPerChannel) { - case TiffBitsPerSample.Bit8: + case 8: { options.ColorType = TiffColorType.BlackIsZero8; break; } - case TiffBitsPerSample.Bit4: + case 4: { options.ColorType = TiffColorType.BlackIsZero4; break; } - case TiffBitsPerSample.Bit1: + case 1: { options.ColorType = TiffColorType.BlackIsZero1; break; @@ -173,30 +184,39 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffPhotometricInterpretation.Rgb: { - if (options.BitsPerSample.Bits().Length != 3) + if (options.BitsPerSample.Channels != 3) { TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } if (options.PlanarConfiguration == TiffPlanarConfiguration.Chunky) { - switch (options.BitsPerSample) + ushort bitsPerChannel = options.BitsPerSample.Channel0; + switch (bitsPerChannel) { - case TiffBitsPerSample.Bit42: + case 16: + options.ColorType = TiffColorType.Rgb161616; + break; + + case 14: options.ColorType = TiffColorType.Rgb141414; break; - case TiffBitsPerSample.Bit30: + case 12: + options.ColorType = TiffColorType.Rgb121212; + break; + + case 10: options.ColorType = TiffColorType.Rgb101010; break; - case TiffBitsPerSample.Bit24: + case 8: options.ColorType = TiffColorType.Rgb888; break; - case TiffBitsPerSample.Bit12: + case 4: options.ColorType = TiffColorType.Rgb444; break; - case TiffBitsPerSample.Bit6: + case 2: options.ColorType = TiffColorType.Rgb222; break; default: @@ -217,7 +237,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff options.ColorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; if (options.ColorMap != null) { - if (options.BitsPerSample.Bits().Length != 1) + if (options.BitsPerSample.Channels != 1) { TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } @@ -291,18 +311,5 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } } - - private static TiffBitsPerSample GetBitsPerSample(TiffBitsPerPixel? bitsPerPixel) => bitsPerPixel switch - { - TiffBitsPerPixel.Bit1 => TiffBitsPerSample.Bit1, - TiffBitsPerPixel.Bit4 => TiffBitsPerSample.Bit4, - TiffBitsPerPixel.Bit6 => TiffBitsPerSample.Bit6, - TiffBitsPerPixel.Bit8 => TiffBitsPerSample.Bit8, - TiffBitsPerPixel.Bit12 => TiffBitsPerSample.Bit12, - TiffBitsPerPixel.Bit24 => TiffBitsPerSample.Bit24, - TiffBitsPerPixel.Bit30 => TiffBitsPerSample.Bit30, - TiffBitsPerPixel.Bit42 => TiffBitsPerSample.Bit42, - _ => throw new NotSupportedException("The bits per pixel are not supported"), - }; } } diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index d5137c4357..2273d759f0 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -320,10 +320,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.SetEncoderOptions(bitsPerPixel, photometricInterpretation ?? TiffPhotometricInterpretation.BlackIsZero, compression, predictor); break; case TiffBitsPerPixel.Bit6: + case TiffBitsPerPixel.Bit10: case TiffBitsPerPixel.Bit12: + case TiffBitsPerPixel.Bit14: + case TiffBitsPerPixel.Bit16: case TiffBitsPerPixel.Bit30: + case TiffBitsPerPixel.Bit36: case TiffBitsPerPixel.Bit42: - // Encoding 42, 30, 12 and 6 bits per pixel is not yet supported. Default to 24 bits. + case TiffBitsPerPixel.Bit48: + // Encoding not yet supported bits per pixel will default to 24 bits. this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); break; default: diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index 9bc0792c40..43a0868496 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -318,34 +318,34 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffPhotometricInterpretation.PaletteColor: if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit4) { - return TiffConstants.BitsPerSample4Bit; + return TiffConstants.BitsPerSample4Bit.ToArray(); } else { - return TiffConstants.BitsPerSample8Bit; + return TiffConstants.BitsPerSample8Bit.ToArray(); } case TiffPhotometricInterpretation.Rgb: - return TiffConstants.BitsPerSampleRgb8Bit; + return TiffConstants.BitsPerSampleRgb8Bit.ToArray(); case TiffPhotometricInterpretation.WhiteIsZero: if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1) { - return TiffConstants.BitsPerSample1Bit; + return TiffConstants.BitsPerSample1Bit.ToArray(); } - return TiffConstants.BitsPerSample8Bit; + return TiffConstants.BitsPerSample8Bit.ToArray(); case TiffPhotometricInterpretation.BlackIsZero: if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1) { - return TiffConstants.BitsPerSample1Bit; + return TiffConstants.BitsPerSample1Bit.ToArray(); } - return TiffConstants.BitsPerSample8Bit; + return TiffConstants.BitsPerSample8Bit.ToArray(); default: - return TiffConstants.BitsPerSampleRgb8Bit; + return TiffConstants.BitsPerSampleRgb8Bit.ToArray(); } } diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs index 25a0578e91..002dbf039e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs +++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs @@ -35,6 +35,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public TiffBitsPerPixel? BitsPerPixel { get; set; } + /// + /// Gets or sets number of bits per component. + /// + public TiffBitsPerSample? BitsPerSample { get; set; } + /// /// Gets or sets the compression scheme used on the image data. /// @@ -64,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff } /// - /// Parses the given Exif profile to populate the properties of the tiff frame meta data.. + /// Parses the given Exif profile to populate the properties of the tiff frame meta data. /// /// The tiff frame meta data. /// The Exif profile containing tiff frame directory tags. @@ -72,11 +77,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff { if (profile != null) { - ushort[] bitsPerSample = profile.GetValue(ExifTag.BitsPerSample)?.Value; - meta.BitsPerPixel = BitsPerPixelFromBitsPerSample(bitsPerSample); + if (TiffBitsPerSample.TryParse(profile.GetValue(ExifTag.BitsPerSample)?.Value, out TiffBitsPerSample bitsPerSample)) + { + meta.BitsPerSample = bitsPerSample; + } + + meta.BitsPerPixel = meta.BitsPerSample?.BitsPerPixel(); meta.Compression = (TiffCompression?)profile.GetValue(ExifTag.Compression)?.Value; - meta.PhotometricInterpretation = - (TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value; + meta.PhotometricInterpretation = (TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value; meta.Predictor = (TiffPredictor?)profile.GetValue(ExifTag.Predictor)?.Value; profile.RemoveValue(ExifTag.BitsPerSample); @@ -86,27 +94,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff } } - /// - /// Gets the bits per pixel for the given bits per sample. - /// - /// The tiff bits per sample. - /// Bits per pixel. - private static TiffBitsPerPixel? BitsPerPixelFromBitsPerSample(ushort[] bitsPerSample) - { - if (bitsPerSample == null) - { - return null; - } - - int bitsPerPixel = 0; - foreach (ushort bits in bitsPerSample) - { - bitsPerPixel += bits; - } - - return (TiffBitsPerPixel)bitsPerPixel; - } - /// public IDeepCloneable DeepClone() => new TiffFrameMetadata(this); } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter{TPixel}.cs index 232daa18d6..7100fe9fc8 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter{TPixel}.cs @@ -79,10 +79,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers this.Dispose(true); } - protected static Span GetStripPixels(Buffer2D buffer2D, int y, int height) - where T : struct - => buffer2D.GetSingleSpan().Slice(y * buffer2D.Width, height * buffer2D.Width); - protected abstract void EncodeStrip(int y, int height, TiffBaseCompressor compressor); /// diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs index be5c837eae..662e729ef9 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter{TPixel}.cs @@ -36,38 +36,50 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers /// protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) { - this.pixelsAsGray ??= this.MemoryAllocator.Allocate(height * this.Image.Width); - - Span pixelAsGraySpan = this.pixelsAsGray.Slice(0, height * this.Image.Width); - - Span pixelsBlackWhite = GetStripPixels(this.imageBlackWhite.GetRootFramePixelBuffer(), y, height); - - PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhite, pixelAsGraySpan, pixelsBlackWhite.Length); + int width = this.Image.Width; if (compressor.Method == TiffCompression.CcittGroup3Fax || compressor.Method == TiffCompression.Ccitt1D) { // Special case for T4BitCompressor. - compressor.CompressStrip(pixelAsGraySpan, height); + int stripPixels = width * height; + this.pixelsAsGray ??= this.MemoryAllocator.Allocate(stripPixels); + Span pixelAsGraySpan = this.pixelsAsGray.GetSpan(); + int lastRow = y + height; + int grayRowIdx = 0; + for (int row = y; row < lastRow; row++) + { + Span pixelsBlackWhiteRow = this.imageBlackWhite.GetPixelRowSpan(row); + Span pixelAsGrayRow = pixelAsGraySpan.Slice(grayRowIdx * width, width); + PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhiteRow, pixelAsGrayRow, width); + grayRowIdx++; + } + + compressor.CompressStrip(pixelAsGraySpan.Slice(0, stripPixels), height); } else { // Write uncompressed image. int bytesPerStrip = this.BytesPerRow * height; this.bitStrip ??= this.MemoryAllocator.AllocateManagedByteBuffer(bytesPerStrip); + this.pixelsAsGray ??= this.MemoryAllocator.Allocate(width); + Span pixelAsGraySpan = this.pixelsAsGray.GetSpan(); Span rows = this.bitStrip.Slice(0, bytesPerStrip); rows.Clear(); - int grayPixelIndex = 0; - for (int s = 0; s < height; s++) + int outputRowIdx = 0; + int lastRow = y + height; + for (int row = y; row < lastRow; row++) { int bitIndex = 0; int byteIndex = 0; - Span outputRow = rows.Slice(s * this.BytesPerRow); + Span outputRow = rows.Slice(outputRowIdx * this.BytesPerRow); + Span pixelsBlackWhiteRow = this.imageBlackWhite.GetPixelRowSpan(row); + PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelsBlackWhiteRow, pixelAsGraySpan, width); for (int x = 0; x < this.Image.Width; x++) { int shift = 7 - bitIndex; - if (pixelAsGraySpan[grayPixelIndex++] == 255) + if (pixelAsGraySpan[x] == 255) { outputRow[byteIndex] |= (byte)(1 << shift); } @@ -79,6 +91,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers bitIndex = 0; } } + + outputRowIdx++; } compressor.CompressStrip(rows, height); diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter{TPixel}.cs index 4df57f7e85..43cb666b6a 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffCompositeColorWriter{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -30,12 +31,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers this.rowBuffer.Clear(); - Span rowSpan = this.rowBuffer.GetSpan().Slice(0, this.BytesPerRow * height); + Span outputRowSpan = this.rowBuffer.GetSpan().Slice(0, this.BytesPerRow * height); - Span pixels = GetStripPixels(this.Image.PixelBuffer, y, height); + int width = this.Image.Width; + using IMemoryOwner stripPixelBuffer = this.MemoryAllocator.Allocate(height * width); + Span stripPixels = stripPixelBuffer.GetSpan(); + int lastRow = y + height; + int stripPixelsRowIdx = 0; + for (int row = y; row < lastRow; row++) + { + Span stripPixelsRow = this.Image.PixelBuffer.GetRowSpan(row); + stripPixelsRow.CopyTo(stripPixels.Slice(stripPixelsRowIdx * width, width)); + stripPixelsRowIdx++; + } - this.EncodePixels(pixels, rowSpan); - compressor.CompressStrip(rowSpan, height); + this.EncodePixels(stripPixels, outputRowSpan); + compressor.CompressStrip(outputRowSpan, height); } protected abstract void EncodePixels(Span pixels, Span buffer); diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs index d1a3dd1ea3..61e24d6529 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs @@ -5,7 +5,6 @@ using System; using System.Buffers; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -21,6 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers private readonly int colorPaletteSize; private readonly int colorPaletteBytes; private readonly IndexedImageFrame quantizedImage; + private IMemoryOwner indexedPixelsBuffer; public TiffPaletteWriter( ImageFrame image, @@ -55,22 +55,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers /// protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) { - Span indexedPixels = GetStripPixels(((IPixelSource)this.quantizedImage).PixelBuffer, y, height); + int width = this.Image.Width; + if (this.BitsPerPixel == 4) { - int width = this.Image.Width; int halfWidth = width >> 1; int excess = (width & 1) * height; // (width % 2) * height int rows4BitBufferLength = (halfWidth * height) + excess; - using IMemoryOwner rows4bitBuffer = this.MemoryAllocator.Allocate(rows4BitBufferLength); - Span rows4bit = rows4bitBuffer.GetSpan(); - int idxPixels = 0; + this.indexedPixelsBuffer ??= this.MemoryAllocator.Allocate(rows4BitBufferLength); + Span rows4bit = this.indexedPixelsBuffer.GetSpan(); int idx4bitRows = 0; - for (int row = 0; row < height; row++) + int lastRow = y + height; + for (int row = y; row < lastRow; row++) { + ReadOnlySpan indexedPixelRow = this.quantizedImage.GetPixelRowSpan(row); + int idxPixels = 0; for (int x = 0; x < halfWidth; x++) { - rows4bit[idx4bitRows] = (byte)((indexedPixels[idxPixels] << 4) | (indexedPixels[idxPixels + 1] & 0xF)); + rows4bit[idx4bitRows] = (byte)((indexedPixelRow[idxPixels] << 4) | (indexedPixelRow[idxPixels + 1] & 0xF)); idxPixels += 2; idx4bitRows++; } @@ -78,7 +80,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers // Make sure rows are byte-aligned. if (width % 2 != 0) { - rows4bit[idx4bitRows++] = (byte)(indexedPixels[idxPixels++] << 4); + rows4bit[idx4bitRows++] = (byte)(indexedPixelRow[idxPixels] << 4); } } @@ -86,12 +88,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers } else { - compressor.CompressStrip(indexedPixels, height); + int stripPixels = width * height; + this.indexedPixelsBuffer ??= this.MemoryAllocator.AllocateManagedByteBuffer(stripPixels); + Span indexedPixels = this.indexedPixelsBuffer.GetSpan(); + int lastRow = y + height; + int indexedPixelsRowIdx = 0; + for (int row = y; row < lastRow; row++) + { + ReadOnlySpan indexedPixelRow = this.quantizedImage.GetPixelRowSpan(row); + indexedPixelRow.CopyTo(indexedPixels.Slice(indexedPixelsRowIdx * width, width)); + indexedPixelsRowIdx++; + } + + compressor.CompressStrip(indexedPixels.Slice(0, stripPixels), height); } } /// - protected override void Dispose(bool disposing) => this.quantizedImage?.Dispose(); + protected override void Dispose(bool disposing) + { + this.quantizedImage?.Dispose(); + this.indexedPixelsBuffer?.Dispose(); + } private void AddColorMapTag() { diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 510f34dc7d..7719b12420 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -12,7 +12,7 @@ $(RepositoryUrl) Image Resize Crop Gif Jpg Jpeg Bitmap Png Tga NetCore A new, fully featured, fully managed, cross-platform, 2D graphics API for .NET - Debug;Release;Release-InnerLoop;Debug-InnerLoop + Debug;Release;Debug-InnerLoop;Release-InnerLoop diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index af86f49b09..9c17881452 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -22,8 +22,8 @@ - - + + diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs index 39055faf50..025412adcd 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs ImageCodecInfo codec = FindCodecForType("image/tiff"); using var parameters = new EncoderParameters(1) { - Param = {[0] = new EncoderParameter(Encoder.Compression, (long)Cast(this.Compression))} + Param = { [0] = new EncoderParameter(Encoder.Compression, (long)Cast(this.Compression)) } }; using var memoryStream = new MemoryStream(); diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs index 47c6f2c7d4..d472791e43 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs @@ -47,8 +47,11 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg this.bmpDrawing = SDImage.FromStream(this.bmpStream); this.jpegCodec = GetEncoder(ImageFormat.Jpeg); this.encoderParameters = new EncoderParameters(1); + // Quality cast to long is necessary +#pragma warning disable IDE0004 // Remove Unnecessary Cast this.encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, (long)this.Quality); +#pragma warning restore IDE0004 // Remove Unnecessary Cast this.destinationStream = new MemoryStream(); } @@ -101,6 +104,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg return codec; } } + return null; } } diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index a146dc03ee..17f6068d40 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -8,7 +8,7 @@ false false - Debug;Release;Release-InnerLoop;Debug-InnerLoop + Debug;Release;Debug-InnerLoop;Release-InnerLoop diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj index fe3b16450c..a60ac604f1 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj @@ -12,7 +12,7 @@ false false - Debug;Release;Release-InnerLoop;Debug-InnerLoop + Debug;Release;Debug-InnerLoop;Release-InnerLoop false diff --git a/tests/ImageSharp.Tests/Common/NumericsTests.cs b/tests/ImageSharp.Tests/Common/NumericsTests.cs new file mode 100644 index 0000000000..29eae6d488 --- /dev/null +++ b/tests/ImageSharp.Tests/Common/NumericsTests.cs @@ -0,0 +1,73 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.Common +{ + public class NumericsTests + { + private ITestOutputHelper Output { get; } + + public NumericsTests(ITestOutputHelper output) + { + this.Output = output; + } + + private static int Log2_ReferenceImplementation(uint value) + { + int n = 0; + while ((value >>= 1) != 0) + { + ++n; + } + + return n; + } + + [Fact] + public void Log2_ZeroConvention() + { + uint value = 0; + int expected = 0; + int actual = Numerics.Log2(value); + + Assert.True(expected == actual, $"Expected: {expected}, Actual: {actual}"); + } + + [Fact] + public void Log2_PowersOfTwo() + { + for (int i = 0; i < sizeof(int) * 8; i++) + { + // from 2^0 to 2^32 + uint value = (uint)(1 << i); + int expected = i; + int actual = Numerics.Log2(value); + + Assert.True(expected == actual, $"Expected: {expected}, Actual: {actual}"); + } + } + + [Theory] + [InlineData(1, 100)] + [InlineData(2, 100)] + public void Log2_RandomValues(int seed, int count) + { + var rng = new Random(seed); + byte[] bytes = new byte[4]; + + for (int i = 0; i < count; i++) + { + rng.NextBytes(bytes); + uint value = BitConverter.ToUInt32(bytes, 0); + int expected = Log2_ReferenceImplementation(value); + int actual = Numerics.Log2(value); + + Assert.True(expected == actual, $"Expected: {expected}, Actual: {actual}"); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index 579ee02901..769ab850e4 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; - +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.PixelFormats; @@ -154,11 +154,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation [MemberData(nameof(BilevelData))] [MemberData(nameof(Grayscale4_Data))] [MemberData(nameof(Grayscale8_Data))] - public void Decode_WritesPixelData(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + public void Decode_WritesPixelData(byte[] inputData, ushort bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) { AssertDecode(expectedResult, pixels => { - new BlackIsZeroTiffColor(new[] { (ushort)bitsPerSample }).Decode(inputData, pixels, left, top, width, height); + new BlackIsZeroTiffColor(new TiffBitsPerSample(bitsPerSample, 0, 0)).Decode(inputData, pixels, left, top, width, height); }); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs index 0da1d8bbd4..e368cd5f1e 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/PaletteTiffColorTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; - +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.PixelFormats; @@ -83,10 +83,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation [Theory] [MemberData(nameof(Palette4Data))] [MemberData(nameof(Palette8Data))] - public void Decode_WritesPixelData(byte[] inputData, ushort bitsPerSample, ushort[] colorMap, int left, int top, int width, int height, Rgba32[][] expectedResult) => AssertDecode(expectedResult, pixels => - { - new PaletteTiffColor(new[] { bitsPerSample }, colorMap).Decode(inputData, pixels, left, top, width, height); - }); + public void Decode_WritesPixelData(byte[] inputData, ushort bitsPerSample, ushort[] colorMap, int left, int top, int width, int height, Rgba32[][] expectedResult) + => AssertDecode(expectedResult, pixels => + { + new PaletteTiffColor(new TiffBitsPerSample(bitsPerSample, 0, 0), colorMap).Decode(inputData, pixels, left, top, width, height); + }); private static uint[][] GeneratePalette(int count) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs index abfae6ab40..e9c73a6683 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColorTests.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; - +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -101,17 +101,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation { get { - yield return new object[] { Rgb4Bytes4X4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Rgb4Result4X4 }; - yield return new object[] { Rgb4Bytes4X4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Offset(Rgb4Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes4X4, new ushort[] { 4, 4, 4 }, 1, 0, 4, 4, Offset(Rgb4Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes4X4, new ushort[] { 4, 4, 4 }, 0, 1, 4, 4, Offset(Rgb4Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4Bytes4X4, new ushort[] { 4, 4, 4 }, 1, 1, 4, 4, Offset(Rgb4Result4X4, 1, 1, 6, 6) }; - - yield return new object[] { Rgb4Bytes3X4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Rgb4Result3X4 }; - yield return new object[] { Rgb4Bytes3X4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Offset(Rgb4Result3X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes3X4, new ushort[] { 4, 4, 4 }, 1, 0, 3, 4, Offset(Rgb4Result3X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes3X4, new ushort[] { 4, 4, 4 }, 0, 1, 3, 4, Offset(Rgb4Result3X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4Bytes3X4, new ushort[] { 4, 4, 4 }, 1, 1, 3, 4, Offset(Rgb4Result3X4, 1, 1, 6, 6) }; + yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 4, 4, Rgb4Result4X4 }; + yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 4, 4, Offset(Rgb4Result4X4, 0, 0, 6, 6) }; + yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 1, 0, 4, 4, Offset(Rgb4Result4X4, 1, 0, 6, 6) }; + yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 1, 4, 4, Offset(Rgb4Result4X4, 0, 1, 6, 6) }; + yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 1, 1, 4, 4, Offset(Rgb4Result4X4, 1, 1, 6, 6) }; + + yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 3, 4, Rgb4Result3X4 }; + yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 3, 4, Offset(Rgb4Result3X4, 0, 0, 6, 6) }; + yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 1, 0, 3, 4, Offset(Rgb4Result3X4, 1, 0, 6, 6) }; + yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 1, 3, 4, Offset(Rgb4Result3X4, 0, 1, 6, 6) }; + yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 1, 1, 3, 4, Offset(Rgb4Result3X4, 1, 1, 6, 6) }; } } @@ -170,11 +170,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation { get { - yield return new object[] { Rgb8Bytes4X4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Rgb8Result4X4 }; - yield return new object[] { Rgb8Bytes4X4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Offset(Rgb8Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb8Bytes4X4, new ushort[] { 8, 8, 8 }, 1, 0, 4, 4, Offset(Rgb8Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb8Bytes4X4, new ushort[] { 8, 8, 8 }, 0, 1, 4, 4, Offset(Rgb8Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb8Bytes4X4, new ushort[] { 8, 8, 8 }, 1, 1, 4, 4, Offset(Rgb8Result4X4, 1, 1, 6, 6) }; + yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 0, 4, 4, Rgb8Result4X4 }; + yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 0, 4, 4, Offset(Rgb8Result4X4, 0, 0, 6, 6) }; + yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 1, 0, 4, 4, Offset(Rgb8Result4X4, 1, 0, 6, 6) }; + yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 1, 4, 4, Offset(Rgb8Result4X4, 0, 1, 6, 6) }; + yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 1, 1, 4, 4, Offset(Rgb8Result4X4, 1, 1, 6, 6) }; } } @@ -230,11 +230,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation { get { - yield return new object[] { Rgb484Bytes4X4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Rgb484Result4X4 }; - yield return new object[] { Rgb484Bytes4X4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Offset(Rgb484Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb484Bytes4X4, new ushort[] { 4, 8, 4 }, 1, 0, 4, 4, Offset(Rgb484Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb484Bytes4X4, new ushort[] { 4, 8, 4 }, 0, 1, 4, 4, Offset(Rgb484Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb484Bytes4X4, new ushort[] { 4, 8, 4 }, 1, 1, 4, 4, Offset(Rgb484Result4X4, 1, 1, 6, 6) }; + yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 0, 4, 4, Rgb484Result4X4 }; + yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 0, 4, 4, Offset(Rgb484Result4X4, 0, 0, 6, 6) }; + yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 1, 0, 4, 4, Offset(Rgb484Result4X4, 1, 0, 6, 6) }; + yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 1, 4, 4, Offset(Rgb484Result4X4, 0, 1, 6, 6) }; + yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 1, 1, 4, 4, Offset(Rgb484Result4X4, 1, 1, 6, 6) }; } } @@ -242,7 +242,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation [MemberData(nameof(Rgb4Data))] [MemberData(nameof(Rgb8Data))] [MemberData(nameof(Rgb484_Data))] - public void Decode_WritesPixelData(byte[][] inputData, ushort[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + public void Decode_WritesPixelData(byte[][] inputData, TiffBitsPerSample bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) { AssertDecode(expectedResult, pixels => { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs index 4abde8f17e..9adf59e484 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/RgbTiffColorTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; - +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.PixelFormats; @@ -63,17 +63,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation { get { - yield return new object[] { Rgb4Bytes4X4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Rgb4Result4X4 }; - yield return new object[] { Rgb4Bytes4X4, new ushort[] { 4, 4, 4 }, 0, 0, 4, 4, Offset(Rgb4Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes4X4, new ushort[] { 4, 4, 4 }, 1, 0, 4, 4, Offset(Rgb4Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes4X4, new ushort[] { 4, 4, 4 }, 0, 1, 4, 4, Offset(Rgb4Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4Bytes4X4, new ushort[] { 4, 4, 4 }, 1, 1, 4, 4, Offset(Rgb4Result4X4, 1, 1, 6, 6) }; - - yield return new object[] { Rgb4Bytes3X4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Rgb4Result3X4 }; - yield return new object[] { Rgb4Bytes3X4, new ushort[] { 4, 4, 4 }, 0, 0, 3, 4, Offset(Rgb4Result3X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes3X4, new ushort[] { 4, 4, 4 }, 1, 0, 3, 4, Offset(Rgb4Result3X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb4Bytes3X4, new ushort[] { 4, 4, 4 }, 0, 1, 3, 4, Offset(Rgb4Result3X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb4Bytes3X4, new ushort[] { 4, 4, 4 }, 1, 1, 3, 4, Offset(Rgb4Result3X4, 1, 1, 6, 6) }; + yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 4, 4, Rgb4Result4X4 }; + yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 4, 4, Offset(Rgb4Result4X4, 0, 0, 6, 6) }; + yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 1, 0, 4, 4, Offset(Rgb4Result4X4, 1, 0, 6, 6) }; + yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 0, 1, 4, 4, Offset(Rgb4Result4X4, 0, 1, 6, 6) }; + yield return new object[] { Rgb4Bytes4X4, new TiffBitsPerSample(4, 4, 4), 1, 1, 4, 4, Offset(Rgb4Result4X4, 1, 1, 6, 6) }; + + yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 3, 4, Rgb4Result3X4 }; + yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 0, 3, 4, Offset(Rgb4Result3X4, 0, 0, 6, 6) }; + yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 1, 0, 3, 4, Offset(Rgb4Result3X4, 1, 0, 6, 6) }; + yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 0, 1, 3, 4, Offset(Rgb4Result3X4, 0, 1, 6, 6) }; + yield return new object[] { Rgb4Bytes3X4, new TiffBitsPerSample(4, 4, 4), 1, 1, 3, 4, Offset(Rgb4Result3X4, 1, 1, 6, 6) }; } } @@ -111,11 +111,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation { get { - yield return new object[] { Rgb8Bytes4X4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Rgb8Result4X4 }; - yield return new object[] { Rgb8Bytes4X4, new ushort[] { 8, 8, 8 }, 0, 0, 4, 4, Offset(Rgb8Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb8Bytes4X4, new ushort[] { 8, 8, 8 }, 1, 0, 4, 4, Offset(Rgb8Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb8Bytes4X4, new ushort[] { 8, 8, 8 }, 0, 1, 4, 4, Offset(Rgb8Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb8Bytes4X4, new ushort[] { 8, 8, 8 }, 1, 1, 4, 4, Offset(Rgb8Result4X4, 1, 1, 6, 6) }; + yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 0, 4, 4, Rgb8Result4X4 }; + yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 0, 4, 4, Offset(Rgb8Result4X4, 0, 0, 6, 6) }; + yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 1, 0, 4, 4, Offset(Rgb8Result4X4, 1, 0, 6, 6) }; + yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 0, 1, 4, 4, Offset(Rgb8Result4X4, 0, 1, 6, 6) }; + yield return new object[] { Rgb8Bytes4X4, new TiffBitsPerSample(8, 8, 8), 1, 1, 4, 4, Offset(Rgb8Result4X4, 1, 1, 6, 6) }; } } @@ -153,11 +153,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation { get { - yield return new object[] { Rgb484Bytes4X4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Rgb484Result4X4 }; - yield return new object[] { Rgb484Bytes4X4, new ushort[] { 4, 8, 4 }, 0, 0, 4, 4, Offset(Rgb484Result4X4, 0, 0, 6, 6) }; - yield return new object[] { Rgb484Bytes4X4, new ushort[] { 4, 8, 4 }, 1, 0, 4, 4, Offset(Rgb484Result4X4, 1, 0, 6, 6) }; - yield return new object[] { Rgb484Bytes4X4, new ushort[] { 4, 8, 4 }, 0, 1, 4, 4, Offset(Rgb484Result4X4, 0, 1, 6, 6) }; - yield return new object[] { Rgb484Bytes4X4, new ushort[] { 4, 8, 4 }, 1, 1, 4, 4, Offset(Rgb484Result4X4, 1, 1, 6, 6) }; + yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 0, 4, 4, Rgb484Result4X4 }; + yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 0, 4, 4, Offset(Rgb484Result4X4, 0, 0, 6, 6) }; + yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 1, 0, 4, 4, Offset(Rgb484Result4X4, 1, 0, 6, 6) }; + yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 0, 1, 4, 4, Offset(Rgb484Result4X4, 0, 1, 6, 6) }; + yield return new object[] { Rgb484Bytes4X4, new TiffBitsPerSample(4, 8, 4), 1, 1, 4, 4, Offset(Rgb484Result4X4, 1, 1, 6, 6) }; } } @@ -165,7 +165,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation [MemberData(nameof(Rgb4Data))] [MemberData(nameof(Rgb8Data))] [MemberData(nameof(Rgb484Data))] - public void Decode_WritesPixelData(byte[] inputData, ushort[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + public void Decode_WritesPixelData(byte[] inputData, TiffBitsPerSample bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) { AssertDecode(expectedResult, pixels => { @@ -175,7 +175,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation [Theory] [MemberData(nameof(Rgb8Data))] - public void Decode_WritesPixelData_8Bit(byte[] inputData, ushort[] bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + public void Decode_WritesPixelData_8Bit(byte[] inputData, TiffBitsPerSample bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) { AssertDecode(expectedResult, pixels => { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs index 620fddd7d0..1d3304e4c8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColorTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; - +using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.PixelFormats; @@ -154,11 +154,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation [MemberData(nameof(BilevelData))] [MemberData(nameof(Grayscale4Data))] [MemberData(nameof(Grayscale8Data))] - public void Decode_WritesPixelData(byte[] inputData, int bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) + public void Decode_WritesPixelData(byte[] inputData, ushort bitsPerSample, int left, int top, int width, int height, Rgba32[][] expectedResult) { AssertDecode(expectedResult, pixels => { - new WhiteIsZeroTiffColor(new[] { (ushort)bitsPerSample }).Decode(inputData, pixels, left, top, width, height); + new WhiteIsZeroTiffColor(new TiffBitsPerSample(bitsPerSample, 0, 0)).Decode(inputData, pixels, left, top, width, height); }); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 02b7f97d94..6b82f4281c 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -99,30 +99,73 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffDecoder_CanDecode_4Bit_WithPalette(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider, ReferenceDecoder, useExactComparer: false, 0.01f); + [Theory] + [WithFile(Flower2BitPalette, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_2Bit_WithPalette(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider, ReferenceDecoder, useExactComparer: false, 0.01f); + + [Theory] + [WithFile(Flower2BitGray, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_2Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] [WithFile(FlowerRgb222Contiguous, PixelTypes.Rgba32)] [WithFile(FlowerRgb222Planar, PixelTypes.Rgba32)] + [WithFile(Flower6BitGray, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_6Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] + [WithFile(Flower8BitGray, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_8Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + + [Theory] + [WithFile(Flower10BitGray, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_10Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] [WithFile(FlowerRgb444Contiguous, PixelTypes.Rgba32)] [WithFile(FlowerRgb444Planar, PixelTypes.Rgba32)] + [WithFile(Flower12BitGray, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_12Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] + [WithFile(Flower14BitGray, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_14Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + + [Theory] + [WithFile(Flower16BitGray, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_16Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] [WithFile(FlowerRgb101010Contiguous, PixelTypes.Rgba32)] [WithFile(FlowerRgb101010Planar, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_30Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] + [WithFile(FlowerRgb121212Contiguous, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_36Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] [WithFile(FlowerRgb141414Contiguous, PixelTypes.Rgba32)] [WithFile(FlowerRgb141414Planar, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_42Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] + [WithFile(FlowerRgb161616Contiguous, PixelTypes.Rgba32)] + [WithFile(FlowerRgb161616Planar, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_48Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] [WithFile(GrayscaleDeflateMultistrip, PixelTypes.Rgba32)] [WithFile(RgbDeflateMultistrip, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index f2f1470f94..0bb9b95b9a 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -78,9 +78,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } [Theory] + [InlineData(TiffBitsPerPixel.Bit48)] [InlineData(TiffBitsPerPixel.Bit42)] + [InlineData(TiffBitsPerPixel.Bit36)] [InlineData(TiffBitsPerPixel.Bit30)] [InlineData(TiffBitsPerPixel.Bit12)] + [InlineData(TiffBitsPerPixel.Bit10)] [InlineData(TiffBitsPerPixel.Bit6)] public void EncoderOptions_UnsupportedBitPerPixel_DefaultTo24Bits(TiffBitsPerPixel bitsPerPixel) { diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index ab350f720e..c80d9fc165 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff { Assert.NotNull(frameMetaData); Assert.NotNull(frameMetaData.BitsPerPixel); - Assert.Equal(TiffBitsPerSample.Bit4, (TiffBitsPerSample)frameMetaData.BitsPerPixel); + Assert.Equal(TiffBitsPerPixel.Bit4, frameMetaData.BitsPerPixel); Assert.Equal(TiffCompression.Lzw, frameMetaData.Compression); Assert.Equal(TiffPhotometricInterpretation.PaletteColor, frameMetaData.PhotometricInterpretation); Assert.Equal(TiffPredictor.None, frameMetaData.Predictor); diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index b6482455e0..b8d44d0d1e 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -6,7 +6,7 @@ SixLabors.ImageSharp.Tests AnyCPU;x64;x86 SixLabors.ImageSharp.Tests - Debug;Release;Release-InnerLoop;Debug-InnerLoop + Debug;Release;Debug-InnerLoop;Release-InnerLoop diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index 2351cbb917..4ab053a310 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -11,6 +11,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; using Xunit.Abstractions; @@ -154,8 +155,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution appendSourceFileOrDescription: false); [Theory] - [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32)] - public void BokehBlurFilterProcessor_Bounded(TestImageProvider provider, BokehBlurInfo value) + [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32, HwIntrinsics.AllowAll)] + [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32, HwIntrinsics.DisableSSE41)] + public void BokehBlurFilterProcessor_Bounded(TestImageProvider provider, BokehBlurInfo value, HwIntrinsics intrinsicsFilter) { static void RunTest(string arg1, string arg2) { @@ -173,12 +175,13 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution x.BokehBlur(value.Radius, value.Components, value.Gamma, bounds); }, testOutputDetails: value.ToString(), + ImageComparer.TolerantPercentage(0.05f), appendPixelTypeToFileName: false); } FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.DisableSSE41, + intrinsicsFilter, provider, value); } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 9471a63937..7eca4795df 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -558,16 +558,27 @@ namespace SixLabors.ImageSharp.Tests public const string RgbPalette = "Tiff/rgb_palette.tiff"; public const string Rgb4BitPalette = "Tiff/bike_colorpalette_4bit.tiff"; public const string RgbPaletteDeflate = "Tiff/rgb_palette_deflate.tiff"; - public const string Flower4BitPalette = "Tiff/flower-palette-04.tiff"; - public const string Flower4BitPaletteGray = "Tiff/flower-minisblack-04.tiff"; + public const string FlowerRgb161616Contiguous = "Tiff/flower-rgb-contig-16.tiff"; + public const string FlowerRgb161616Planar = "Tiff/flower-rgb-planar-16.tiff"; public const string FlowerRgb141414Contiguous = "Tiff/flower-rgb-contig-14.tiff"; public const string FlowerRgb141414Planar = "Tiff/flower-rgb-planar-14.tiff"; public const string FlowerRgb101010Contiguous = "Tiff/flower-rgb-contig-10.tiff"; public const string FlowerRgb101010Planar = "Tiff/flower-rgb-planar-10.tiff"; + public const string FlowerRgb121212Contiguous = "Tiff/flower-rgb-contig-12.tiff"; public const string FlowerRgb444Contiguous = "Tiff/flower-rgb-contig-04.tiff"; public const string FlowerRgb444Planar = "Tiff/flower-rgb-planar-04.tiff"; public const string FlowerRgb222Contiguous = "Tiff/flower-rgb-contig-02.tiff"; public const string FlowerRgb222Planar = "Tiff/flower-rgb-planar-02.tiff"; + public const string Flower2BitGray = "Tiff/flower-minisblack-02.tiff"; + public const string Flower2BitPalette = "Tiff/flower-palette-02.tiff"; + public const string Flower4BitPalette = "Tiff/flower-palette-04.tiff"; + public const string Flower4BitPaletteGray = "Tiff/flower-minisblack-04.tiff"; + public const string Flower6BitGray = "Tiff/flower-minisblack-06.tiff"; + public const string Flower8BitGray = "Tiff/flower-minisblack-08.tiff"; + public const string Flower10BitGray = "Tiff/flower-minisblack-10.tiff"; + public const string Flower12BitGray = "Tiff/flower-minisblack-12.tiff"; + public const string Flower14BitGray = "Tiff/flower-minisblack-14.tiff"; + public const string Flower16BitGray = "Tiff/flower-minisblack-16.tiff"; public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; public const string SmallRgbLzw = "Tiff/rgb_small_lzw.tiff"; diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index dffbeac497..294bd20fb5 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs MemoryGroup framePixels = frame.PixelBuffer.FastMemoryGroup; using IUnsafePixelCollection pixels = magicFrame.GetPixelsUnsafe(); - if (magicFrame.Depth == 8 || magicFrame.Depth == 4 || magicFrame.Depth == 2 || magicFrame.Depth == 1 || magicFrame.Depth == 10) + if (magicFrame.Depth == 8 || magicFrame.Depth == 6 || magicFrame.Depth == 4 || magicFrame.Depth == 2 || magicFrame.Depth == 1 || magicFrame.Depth == 10 || magicFrame.Depth == 12) { byte[] data = pixels.ToByteArray(PixelMapping.RGBA); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs index 84b9297295..05f4f032bf 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -3,7 +3,7 @@ using System; using System.IO; - +using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; @@ -114,5 +114,20 @@ namespace SixLabors.ImageSharp.Tests IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName); Assert.IsType(expectedDecoderType, decoder); } + + // RemoteExecutor does not work with "dotnet xunit" used to run tests on 32 bit .NET Framework: + // https://github.com/SixLabors/ImageSharp/blob/381dff8640b721a34b1227c970fcf6ad6c5e3e72/ci-test.ps1#L30 + public static bool IsNot32BitNetFramework = !TestEnvironment.IsFramework || TestEnvironment.Is64BitProcess; + + [ConditionalFact(nameof(IsNot32BitNetFramework))] + public void RemoteExecutor_FailingRemoteTestShouldFailLocalTest() + { + static void FailingCode() + { + Assert.False(true); + } + + Assert.ThrowsAny(() => RemoteExecutor.Invoke(FailingCode).Dispose()); + } } } diff --git a/tests/Images/Input/Tiff/flower-minisblack-02.tiff b/tests/Images/Input/Tiff/flower-minisblack-02.tiff new file mode 100644 index 0000000000..d6ce305fe6 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-minisblack-02.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3122afede012fa00b8cb379b2f9125a34a38188c3346ec5e18d3b4bddcbb451b +size 1131 diff --git a/tests/Images/Input/Tiff/flower-minisblack-06.tiff b/tests/Images/Input/Tiff/flower-minisblack-06.tiff new file mode 100644 index 0000000000..53db4e1126 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-minisblack-06.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0c13012d8d35215b01192eb38058db4543486c60b4918beec8719a94d1e208e +size 2679 diff --git a/tests/Images/Input/Tiff/flower-minisblack-08.tiff b/tests/Images/Input/Tiff/flower-minisblack-08.tiff new file mode 100644 index 0000000000..02acb15113 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-minisblack-08.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1268d843a2338409ec3a9f5a5a62e23d38c3a898035619994a02f21eff7590bf +size 3453 diff --git a/tests/Images/Input/Tiff/flower-minisblack-10.tiff b/tests/Images/Input/Tiff/flower-minisblack-10.tiff new file mode 100644 index 0000000000..770197726f --- /dev/null +++ b/tests/Images/Input/Tiff/flower-minisblack-10.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a91d6946730604dd65c63f1653fb33031682f26218de33ebf3d0b362cb6883af +size 4269 diff --git a/tests/Images/Input/Tiff/flower-minisblack-12.tiff b/tests/Images/Input/Tiff/flower-minisblack-12.tiff new file mode 100644 index 0000000000..320083c323 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-minisblack-12.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86fc9309872f4e4668350b95fae315d878ec9658046d738050a2743f5fa44446 +size 5043 diff --git a/tests/Images/Input/Tiff/flower-minisblack-14.tiff b/tests/Images/Input/Tiff/flower-minisblack-14.tiff new file mode 100644 index 0000000000..34fca95b56 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-minisblack-14.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcd07668c73f24c2a13133ac4910b59a568502a6d3762675eef61a7e3b090165 +size 5817 diff --git a/tests/Images/Input/Tiff/flower-minisblack-16.tiff b/tests/Images/Input/Tiff/flower-minisblack-16.tiff new file mode 100644 index 0000000000..0791941f99 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-minisblack-16.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79531a10710dee89b86e2467818b7c03a24ff28ebd98c7bdcc292559671e1887 +size 6591 diff --git a/tests/Images/Input/Tiff/flower-palette-02.tiff b/tests/Images/Input/Tiff/flower-palette-02.tiff new file mode 100644 index 0000000000..eb80e4de85 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-palette-02.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75e74d8816942ff6e9dfda411f9171f0f1dd1a5a88cb1410238b55a2b2aeeb71 +size 1164 diff --git a/tests/Images/Input/Tiff/flower-rgb-contig-12.tiff b/tests/Images/Input/Tiff/flower-rgb-contig-12.tiff new file mode 100644 index 0000000000..c890c777a6 --- /dev/null +++ b/tests/Images/Input/Tiff/flower-rgb-contig-12.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f7a63eb8636e2b1ee39dfda4d0bddfc98bdc9eb94bea2dd657619331fa38b5b +size 14483 diff --git a/tests/Images/Input/Tiff/flower-rgb-contig-16.tiff b/tests/Images/Input/Tiff/flower-rgb-contig-16.tiff new file mode 100644 index 0000000000..125de5b9fd --- /dev/null +++ b/tests/Images/Input/Tiff/flower-rgb-contig-16.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab3d6b619a198ff2e5fdd8f9752bf43c5b03a782625b1f0e3f2cfe0f20c4b24a +size 19177 diff --git a/tests/Images/Input/Tiff/flower-rgb-planar-16.tiff b/tests/Images/Input/Tiff/flower-rgb-planar-16.tiff new file mode 100644 index 0000000000..939fd9471b --- /dev/null +++ b/tests/Images/Input/Tiff/flower-rgb-planar-16.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a143fb6c5792fa7755e06feb757c745ad68944336985dc5be8a0c37247fe36d +size 19177