From dc9bceed233c2263add4c94d11bcd26e5a6fd328 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 08:55:00 +0200 Subject: [PATCH 01/30] Remove old frameworks --- tests/ImageSharp.Benchmarks/Config.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs index 60d0e7661..34978b594 100644 --- a/tests/ImageSharp.Benchmarks/Config.cs +++ b/tests/ImageSharp.Benchmarks/Config.cs @@ -32,17 +32,13 @@ namespace SixLabors.ImageSharp.Benchmarks public class MultiFramework : Config { public MultiFramework() => this.AddJob( - Job.Default.WithRuntime(ClrRuntime.Net472), - Job.Default.WithRuntime(CoreRuntime.Core31), - Job.Default.WithRuntime(CoreRuntime.Core50).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); + Job.Default.WithRuntime(CoreRuntime.Core60).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); } public class ShortMultiFramework : Config { public ShortMultiFramework() => this.AddJob( - Job.Default.WithRuntime(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3), - Job.Default.WithRuntime(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3), - Job.Default.WithRuntime(CoreRuntime.Core50).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); + Job.Default.WithRuntime(CoreRuntime.Core60).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); } public class ShortCore31 : Config From 54ff65c7cff6b1739fde3c06adafe983773cc276 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 08:59:22 +0200 Subject: [PATCH 02/30] Add fax4 image for benchmarks --- tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Tiff/CCITTGroup4.tiff | 3 +++ 3 files changed, 5 insertions(+) create mode 100644 tests/Images/Input/Tiff/CCITTGroup4.tiff diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs index db94fb121..b34640bb1 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs @@ -46,6 +46,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [Params( TestImages.Tiff.CcittFax3AllTermCodes, + TestImages.Tiff.Fax4Compressed2, TestImages.Tiff.HuffmanRleAllMakeupCodes, TestImages.Tiff.Calliphora_GrayscaleUncompressed, TestImages.Tiff.Calliphora_RgbPaletteLzw_Predictor, diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 023efd707..e83d3d156 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -759,6 +759,7 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_HuffmanCompressed = "Tiff/Calliphora_huffman_rle.tiff"; public const string Calliphora_BiColorUncompressed = "Tiff/Calliphora_bicolor_uncompressed.tiff"; public const string Fax4Compressed = "Tiff/basi3p02_fax4.tiff"; + public const string Fax4Compressed2 = "Tiff/CCITTGroup4.tiff"; public const string Fax4CompressedLowerOrderBitsFirst = "Tiff/basi3p02_fax4_lowerOrderBitsFirst.tiff"; public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tiff"; diff --git a/tests/Images/Input/Tiff/CCITTGroup4.tiff b/tests/Images/Input/Tiff/CCITTGroup4.tiff new file mode 100644 index 000000000..20ffcd5d6 --- /dev/null +++ b/tests/Images/Input/Tiff/CCITTGroup4.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:120ad0814f207c45d968b05f7435034ecfee8ac1a0958cd984a070dad31f66f3 +size 11082 From 0cf4d9172f64ad67b06d458df1d257bb62dddfd8 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 09:01:42 +0200 Subject: [PATCH 03/30] Add InliningOptions.ShortMethod for WriteBit methods --- src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs index 08d147526..c78862d86 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Formats.Tiff.Compression { @@ -44,8 +45,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } } + [MethodImpl(InliningOptions.ShortMethod)] public static void WriteBit(Span buffer, int bufferPos, int bitPos) => buffer[bufferPos] |= (byte)(1 << (7 - bitPos)); + [MethodImpl(InliningOptions.ShortMethod)] public static void WriteZeroBit(Span buffer, int bufferPos, int bitPos) => buffer[bufferPos] = (byte)(buffer[bufferPos] & ~(1 << (7 - bitPos))); } } From 8459828aad3bf6cdbac4dcc76faeb8d657120e83 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 09:18:10 +0200 Subject: [PATCH 04/30] Use GetRowSpan to access pixel data --- .../BlackIsZero1TiffColor{TPixel}.cs | 13 ++++++------- .../WhiteIsZero1TiffColor{TPixel}.cs | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs index eb749efe6..6cb9c6176 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs @@ -17,14 +17,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - int offset = 0; + var colorBlack = default(TPixel); + var colorWhite = default(TPixel); - Color black = Color.Black; - Color white = Color.White; + colorBlack.FromRgba32(Color.Black); + colorWhite.FromRgba32(Color.White); for (int y = top; y < top + height; y++) { + Span pixelRowSpan = pixels.DangerousGetRowSpan(y); for (int x = left; x < left + width; x += 8) { byte b = data[offset++]; @@ -34,9 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { int bit = (b >> (7 - shift)) & 1; - color.FromRgba32(bit == 0 ? black : white); - - pixels[x + shift, y] = color; + pixelRowSpan[x + shift] = bit == 0 ? colorBlack : colorWhite; } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs index 5f1afe46f..41152ac72 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs @@ -16,14 +16,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - int offset = 0; + var colorBlack = default(TPixel); + var colorWhite = default(TPixel); - Color black = Color.Black; - Color white = Color.White; + colorBlack.FromRgba32(Color.Black); + colorWhite.FromRgba32(Color.White); for (int y = top; y < top + height; y++) { + Span pixelRowSpan = pixels.DangerousGetRowSpan(y); for (int x = left; x < left + width; x += 8) { byte b = data[offset++]; @@ -33,9 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { int bit = (b >> (7 - shift)) & 1; - color.FromRgba32(bit == 0 ? white : black); - - pixels[x + shift, y] = color; + pixelRowSpan[x + shift] = bit == 0 ? colorWhite : colorBlack; } } } From c0059fc599d40dbb556fd983ad52635afa786099 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 10:03:04 +0200 Subject: [PATCH 05/30] Avoid calculating bit position multiple times --- .../Compression/Decompressors/T4BitReader.cs | 54 +++++++++---------- .../Decompressors/T6TiffCompression.cs | 21 +++++++- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index 9925d5a19..122316f94 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -52,28 +52,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// private readonly int maxCodeLength = 13; - private static readonly Dictionary WhiteLen4TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen4TermCodes = new() { { 0x7, 2 }, { 0x8, 3 }, { 0xB, 4 }, { 0xC, 5 }, { 0xE, 6 }, { 0xF, 7 } }; - private static readonly Dictionary WhiteLen5TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen5TermCodes = new() { { 0x13, 8 }, { 0x14, 9 }, { 0x7, 10 }, { 0x8, 11 } }; - private static readonly Dictionary WhiteLen6TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen6TermCodes = new() { { 0x7, 1 }, { 0x8, 12 }, { 0x3, 13 }, { 0x34, 14 }, { 0x35, 15 }, { 0x2A, 16 }, { 0x2B, 17 } }; - private static readonly Dictionary WhiteLen7TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen7TermCodes = new() { { 0x27, 18 }, { 0xC, 19 }, { 0x8, 20 }, { 0x17, 21 }, { 0x3, 22 }, { 0x4, 23 }, { 0x28, 24 }, { 0x2B, 25 }, { 0x13, 26 }, { 0x24, 27 }, { 0x18, 28 } }; - private static readonly Dictionary WhiteLen8TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen8TermCodes = new() { { 0x35, 0 }, { 0x2, 29 }, { 0x3, 30 }, { 0x1A, 31 }, { 0x1B, 32 }, { 0x12, 33 }, { 0x13, 34 }, { 0x14, 35 }, { 0x15, 36 }, { 0x16, 37 }, { 0x17, 38 }, { 0x28, 39 }, { 0x29, 40 }, { 0x2A, 41 }, { 0x2B, 42 }, { 0x2C, 43 }, { 0x2D, 44 }, { 0x4, 45 }, @@ -81,57 +81,57 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { 0x58, 55 }, { 0x59, 56 }, { 0x5A, 57 }, { 0x5B, 58 }, { 0x4A, 59 }, { 0x4B, 60 }, { 0x32, 61 }, { 0x33, 62 }, { 0x34, 63 } }; - private static readonly Dictionary BlackLen2TermCodes = new Dictionary() + private static readonly Dictionary BlackLen2TermCodes = new() { { 0x3, 2 }, { 0x2, 3 } }; - private static readonly Dictionary BlackLen3TermCodes = new Dictionary() + private static readonly Dictionary BlackLen3TermCodes = new() { { 0x2, 1 }, { 0x3, 4 } }; - private static readonly Dictionary BlackLen4TermCodes = new Dictionary() + private static readonly Dictionary BlackLen4TermCodes = new() { { 0x3, 5 }, { 0x2, 6 } }; - private static readonly Dictionary BlackLen5TermCodes = new Dictionary() + private static readonly Dictionary BlackLen5TermCodes = new() { { 0x3, 7 } }; - private static readonly Dictionary BlackLen6TermCodes = new Dictionary() + private static readonly Dictionary BlackLen6TermCodes = new() { { 0x5, 8 }, { 0x4, 9 } }; - private static readonly Dictionary BlackLen7TermCodes = new Dictionary() + private static readonly Dictionary BlackLen7TermCodes = new() { { 0x4, 10 }, { 0x5, 11 }, { 0x7, 12 } }; - private static readonly Dictionary BlackLen8TermCodes = new Dictionary() + private static readonly Dictionary BlackLen8TermCodes = new() { { 0x4, 13 }, { 0x7, 14 } }; - private static readonly Dictionary BlackLen9TermCodes = new Dictionary() + private static readonly Dictionary BlackLen9TermCodes = new() { { 0x18, 15 } }; - private static readonly Dictionary BlackLen10TermCodes = new Dictionary() + private static readonly Dictionary BlackLen10TermCodes = new() { { 0x37, 0 }, { 0x17, 16 }, { 0x18, 17 }, { 0x8, 18 } }; - private static readonly Dictionary BlackLen11TermCodes = new Dictionary() + private static readonly Dictionary BlackLen11TermCodes = new() { { 0x67, 19 }, { 0x68, 20 }, { 0x6C, 21 }, { 0x37, 22 }, { 0x28, 23 }, { 0x17, 24 }, { 0x18, 25 } }; - private static readonly Dictionary BlackLen12TermCodes = new Dictionary() + private static readonly Dictionary BlackLen12TermCodes = new() { { 0xCA, 26 }, { 0xCB, 27 }, { 0xCC, 28 }, { 0xCD, 29 }, { 0x68, 30 }, { 0x69, 31 }, { 0x6A, 32 }, { 0x6B, 33 }, { 0xD2, 34 }, { 0xD3, 35 }, { 0xD4, 36 }, { 0xD5, 37 }, { 0xD6, 38 }, { 0xD7, 39 }, { 0x6C, 40 }, { 0x6D, 41 }, { 0xDA, 42 }, { 0xDB, 43 }, @@ -140,62 +140,62 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { 0x66, 62 }, { 0x67, 63 } }; - private static readonly Dictionary WhiteLen5MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen5MakeupCodes = new() { { 0x1B, 64 }, { 0x12, 128 } }; - private static readonly Dictionary WhiteLen6MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen6MakeupCodes = new() { { 0x17, 192 }, { 0x18, 1664 } }; - private static readonly Dictionary WhiteLen8MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen8MakeupCodes = new() { { 0x36, 320 }, { 0x37, 384 }, { 0x64, 448 }, { 0x65, 512 }, { 0x68, 576 }, { 0x67, 640 } }; - private static readonly Dictionary WhiteLen7MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen7MakeupCodes = new() { { 0x37, 256 } }; - private static readonly Dictionary WhiteLen9MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen9MakeupCodes = new() { { 0xCC, 704 }, { 0xCD, 768 }, { 0xD2, 832 }, { 0xD3, 896 }, { 0xD4, 960 }, { 0xD5, 1024 }, { 0xD6, 1088 }, { 0xD7, 1152 }, { 0xD8, 1216 }, { 0xD9, 1280 }, { 0xDA, 1344 }, { 0xDB, 1408 }, { 0x98, 1472 }, { 0x99, 1536 }, { 0x9A, 1600 }, { 0x9B, 1728 } }; - private static readonly Dictionary WhiteLen11MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen11MakeupCodes = new() { { 0x8, 1792 }, { 0xC, 1856 }, { 0xD, 1920 } }; - private static readonly Dictionary WhiteLen12MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen12MakeupCodes = new() { { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304 }, { 0x1C, 2368 }, { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 } }; - private static readonly Dictionary BlackLen10MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen10MakeupCodes = new() { { 0xF, 64 } }; - private static readonly Dictionary BlackLen11MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen11MakeupCodes = new() { { 0x8, 1792 }, { 0xC, 1856 }, { 0xD, 1920 } }; - private static readonly Dictionary BlackLen12MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen12MakeupCodes = new() { { 0xC8, 128 }, { 0xC9, 192 }, { 0x5B, 256 }, { 0x33, 320 }, { 0x34, 384 }, { 0x35, 448 }, { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304 }, { 0x1C, 2368 }, { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 } }; - private static readonly Dictionary BlackLen13MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen13MakeupCodes = new() { { 0x6C, 512 }, { 0x6D, 576 }, { 0x4A, 640 }, { 0x4B, 704 }, { 0x4C, 768 }, { 0x4D, 832 }, { 0x72, 896 }, { 0x73, 960 }, { 0x74, 1024 }, { 0x75, 1088 }, { 0x76, 1152 }, { 0x77, 1216 }, { 0x52, 1280 }, { 0x53, 1344 }, diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 972f4d8ff..71d7c4d55 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -77,10 +77,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors private uint WriteScanLine(Span buffer, Span scanLine, uint bitsWritten) { byte white = (byte)(this.isWhiteZero ? 0 : 255); + int bitPos = (int)(bitsWritten % 8); + int bufferPos = (int)(bitsWritten / 8); for (int i = 0; i < scanLine.Length; i++) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, 1, scanLine[i] == white ? this.whiteValue : this.blackValue); + if (scanLine[i] == white) + { + BitWriterUtils.WriteZeroBit(buffer, bufferPos, bitPos); + } + else + { + BitWriterUtils.WriteBit(buffer, bufferPos, bitPos); + } + + bitPos++; bitsWritten++; + + if (bitPos >= 8) + { + bitPos = 0; + bufferPos++; + } } // Write padding bytes, if necessary. @@ -122,7 +139,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } else { - scanline.Fill((byte)255); + scanline.Fill(255); } break; From cecd7f29d77b8d09888437e9e0fc992085e1829f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 10:17:15 +0200 Subject: [PATCH 06/30] Add MethodImpl(InliningOptions.ShortMethod) for SwapColor --- .../Tiff/Compression/Decompressors/T6BitReader.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs index 6b9939b17..e747718a4 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; @@ -16,32 +17,32 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { private readonly int maxCodeLength = 12; - private static readonly CcittTwoDimensionalCode None = new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.None, 0); + private static readonly CcittTwoDimensionalCode None = new(CcittTwoDimensionalCodeType.None, 0); - private static readonly Dictionary Len1Codes = new Dictionary() + private static readonly Dictionary Len1Codes = new() { { 0b1, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Vertical0, 1) } }; - private static readonly Dictionary Len3Codes = new Dictionary() + private static readonly Dictionary Len3Codes = new() { { 0b001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Horizontal, 3) }, { 0b010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL1, 3) }, { 0b011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR1, 3) } }; - private static readonly Dictionary Len4Codes = new Dictionary() + private static readonly Dictionary Len4Codes = new() { { 0b0001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Pass, 4) } }; - private static readonly Dictionary Len6Codes = new Dictionary() + private static readonly Dictionary Len6Codes = new() { { 0b000011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR2, 6) }, { 0b000010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL2, 6) } }; - private static readonly Dictionary Len7Codes = new Dictionary() + private static readonly Dictionary Len7Codes = new() { { 0b0000011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR3, 7) }, { 0b0000010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL3, 7) }, @@ -154,6 +155,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// /// Swaps the white run to black run an vise versa. /// + [MethodImpl(InliningOptions.ShortMethod)] public void SwapColor() => this.IsWhiteRun = !this.IsWhiteRun; } } From 0ae59fdba44e300963b18cee7285bb7691f17388 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 14:01:47 +0200 Subject: [PATCH 07/30] Avoid bounds checks in GetBit() --- .../Formats/Tiff/Compression/Decompressors/T4BitReader.cs | 4 +++- .../Tiff/Compression/Decompressors/T6TiffCompression.cs | 6 ------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index 122316f94..15e2b3c30 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; @@ -815,7 +816,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors Span dataSpan = this.Data.GetSpan(); int shift = 8 - this.BitsRead - 1; - uint bit = (uint)((dataSpan[(int)this.Position] & (1 << shift)) != 0 ? 1 : 0); + ref byte dataAtPosition = ref Unsafe.Add(ref MemoryMarshal.GetReference(dataSpan), (int)this.Position); + uint bit = (uint)((dataAtPosition & (1 << shift)) != 0 ? 1 : 0); this.BitsRead++; return bit; diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 71d7c4d55..c769d4d68 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -15,10 +15,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { private readonly bool isWhiteZero; - private readonly byte whiteValue; - - private readonly byte blackValue; - private readonly int width; /// @@ -40,8 +36,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors this.FillOrder = fillOrder; this.width = width; this.isWhiteZero = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; - this.whiteValue = (byte)(this.isWhiteZero ? 0 : 1); - this.blackValue = (byte)(this.isWhiteZero ? 1 : 0); } /// From 07d2e79c384a45eb9dc26b658a6e1e80ea264713 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 17:15:28 +0200 Subject: [PATCH 08/30] Avoid more bounds checks --- .../Formats/Tiff/Compression/BitWriterUtils.cs | 13 +++++++++++-- .../Compression/Decompressors/T6TiffCompression.cs | 4 +++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs index c78862d86..7394577a5 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Tiff.Compression { @@ -46,9 +47,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } [MethodImpl(InliningOptions.ShortMethod)] - public static void WriteBit(Span buffer, int bufferPos, int bitPos) => buffer[bufferPos] |= (byte)(1 << (7 - bitPos)); + public static void WriteBit(Span buffer, int bufferPos, int bitPos) + { + ref byte b = ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), bufferPos); + b |= (byte)(1 << (7 - bitPos)); + } [MethodImpl(InliningOptions.ShortMethod)] - public static void WriteZeroBit(Span buffer, int bufferPos, int bitPos) => buffer[bufferPos] = (byte)(buffer[bufferPos] & ~(1 << (7 - bitPos))); + public static void WriteZeroBit(Span buffer, int bufferPos, int bitPos) + { + ref byte b = ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), bufferPos); + b = (byte)(b & ~(1 << (7 - bitPos))); + } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index c769d4d68..1ce7f02d1 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -75,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors int bufferPos = (int)(bitsWritten / 8); for (int i = 0; i < scanLine.Length; i++) { - if (scanLine[i] == white) + if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) == white) { BitWriterUtils.WriteZeroBit(buffer, bufferPos, bitPos); } From 57349fd0aea4f3258f7dde682b2776ae05b35137 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 17:16:56 +0200 Subject: [PATCH 09/30] Avoid writing zero bit, we already have a clean buffer --- .../Tiff/Compression/Decompressors/T6TiffCompression.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 1ce7f02d1..f8333cf33 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -77,11 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors int bufferPos = (int)(bitsWritten / 8); for (int i = 0; i < scanLine.Length; i++) { - if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) == white) - { - BitWriterUtils.WriteZeroBit(buffer, bufferPos, bitPos); - } - else + if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) != white) { BitWriterUtils.WriteBit(buffer, bufferPos, bitPos); } From c7aaf10411f23a70d2162721259bea6e076ff861 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 17:43:38 +0200 Subject: [PATCH 10/30] Use GetRowSpan --- .../BlackIsZero4TiffColor{TPixel}.cs | 7 ++++--- .../WhiteIsZero4TiffColor{TPixel}.cs | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs index 2e66bb6d7..79247d8ed 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs @@ -24,6 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var l8 = default(L8); for (int y = top; y < top + height; y++) { + Span pixelRowSpan = pixels.DangerousGetRowSpan(y); for (int x = left; x < left + width - 1;) { byte byteData = data[offset++]; @@ -32,13 +33,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation l8.PackedValue = intensity1; color.FromL8(l8); - pixels[x++, y] = color; + pixelRowSpan[x++] = color; byte intensity2 = (byte)((byteData & 0x0F) * 17); l8.PackedValue = intensity2; color.FromL8(l8); - pixels[x++, y] = color; + pixelRowSpan[x++] = color; } if (isOddWidth) @@ -49,7 +50,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation l8.PackedValue = intensity1; color.FromL8(l8); - pixels[left + width - 1, y] = color; + pixelRowSpan[left + width - 1] = color; } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs index a4650af5e..8b635043e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs @@ -24,6 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var l8 = default(L8); for (int y = top; y < top + height; y++) { + Span pixelRowSpan = pixels.DangerousGetRowSpan(y); for (int x = left; x < left + width - 1;) { byte byteData = data[offset++]; @@ -32,13 +33,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation l8.PackedValue = intensity1; color.FromL8(l8); - pixels[x++, y] = color; + pixelRowSpan[x++] = color; byte intensity2 = (byte)((15 - (byteData & 0x0F)) * 17); l8.PackedValue = intensity2; color.FromL8(l8); - pixels[x++, y] = color; + pixelRowSpan[x++] = color; } if (isOddWidth) @@ -49,7 +50,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation l8.PackedValue = intensity1; color.FromL8(l8); - pixels[left + width - 1, y] = color; + pixelRowSpan[left + width - 1] = color; } } } From 432c0d7262e66b71dd1e49a241c8ca4d1a2979dd Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 18:05:39 +0200 Subject: [PATCH 11/30] Avoid bounds checks in BlackIsZero and WhiteIsZero --- .../BlackIsZero1TiffColor{TPixel}.cs | 9 +++++++-- .../WhiteIsZero1TiffColor{TPixel}.cs | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs index 6cb9c6176..2d8e21d6c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -23,19 +25,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation colorBlack.FromRgba32(Color.Black); colorWhite.FromRgba32(Color.White); + ref byte dataRef = ref MemoryMarshal.GetReference(data); for (int y = top; y < top + height; y++) { Span pixelRowSpan = pixels.DangerousGetRowSpan(y); + ref TPixel pixelRowRef = ref MemoryMarshal.GetReference(pixelRowSpan); for (int x = left; x < left + width; x += 8) { - byte b = data[offset++]; + byte b = Unsafe.Add(ref dataRef, offset++); int maxShift = Math.Min(left + width - x, 8); for (int shift = 0; shift < maxShift; shift++) { int bit = (b >> (7 - shift)) & 1; - pixelRowSpan[x + shift] = bit == 0 ? colorBlack : colorWhite; + ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); + pixel = bit == 0 ? colorBlack : colorWhite; } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs index 41152ac72..ad7d6c08a 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -22,19 +24,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation colorBlack.FromRgba32(Color.Black); colorWhite.FromRgba32(Color.White); + ref byte dataRef = ref MemoryMarshal.GetReference(data); for (int y = top; y < top + height; y++) { Span pixelRowSpan = pixels.DangerousGetRowSpan(y); + ref TPixel pixelRowRef = ref MemoryMarshal.GetReference(pixelRowSpan); for (int x = left; x < left + width; x += 8) { - byte b = data[offset++]; + byte b = Unsafe.Add(ref dataRef, offset++); int maxShift = Math.Min(left + width - x, 8); for (int shift = 0; shift < maxShift; shift++) { int bit = (b >> (7 - shift)) & 1; - pixelRowSpan[x + shift] = bit == 0 ? colorWhite : colorBlack; + ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); + pixel = bit == 0 ? colorWhite : colorBlack; } } } From 2876f2f44d0bc363450eda0f81da6db9da7fbb4d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 23:37:28 +0200 Subject: [PATCH 12/30] Use nint, determine what's white only once --- .../Compression/Decompressors/CcittReferenceScanline.cs | 2 ++ .../Tiff/Compression/Decompressors/T6TiffCompression.cs | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittReferenceScanline.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittReferenceScanline.cs index 0aec2361c..5b0c64cf1 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittReferenceScanline.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittReferenceScanline.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { @@ -130,6 +131,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors return index + offset; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int FindB2ForImaginaryWhiteLine() => this.width; private int FindB2ForNormalLine(int b1) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index f8333cf33..e9921418d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors private readonly int width; + private readonly byte white; + /// /// Initializes a new instance of the class. /// @@ -38,6 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors this.FillOrder = fillOrder; this.width = width; this.isWhiteZero = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; + this.white = (byte)(this.isWhiteZero ? 0 : 255); } /// @@ -72,12 +75,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors private uint WriteScanLine(Span buffer, Span scanLine, uint bitsWritten) { - byte white = (byte)(this.isWhiteZero ? 0 : 255); int bitPos = (int)(bitsWritten % 8); int bufferPos = (int)(bitsWritten / 8); - for (int i = 0; i < scanLine.Length; i++) + for (nint i = 0; i < scanLine.Length; i++) { - if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) != white) + if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) != this.white) { BitWriterUtils.WriteBit(buffer, bufferPos, bitPos); } From a26bfe01236444c9b2129bb53d2df3f03a0b1a45 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 May 2022 09:01:28 +0200 Subject: [PATCH 13/30] Access data span only for new byte, not for every bit --- .../Decompressors/ModifiedHuffmanBitReader.cs | 3 +- .../Compression/Decompressors/T4BitReader.cs | 36 ++++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs index 89cdf7ea2..4a87e2951 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs @@ -57,8 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors if (remainder != 0) { // Skip padding bits, move to next byte. - this.Position++; - this.ResetBitsRead(); + this.AdvancePosition(); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index 15e2b3c30..1053ca107 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -229,12 +229,20 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors this.RunLength = 0; this.eolPadding = eolPadding; + Span dataSpan = this.Data.GetSpan(); + this.DataAtPosition = dataSpan[(int)this.Position]; + if (this.eolPadding) { this.maxCodeLength = 24; } } + /// + /// Gets or sets the byte at the given position. + /// + private byte DataAtPosition { get; set; } + /// /// Gets the current value. /// @@ -453,6 +461,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors return v; } + /// + /// Advances the position by one byte. + /// + /// True, if data could be advanced by one byte. + protected bool AdvancePosition() + { + this.LoadNewByte(); + if (this.Position < (ulong)this.DataLength) + { + Span dataSpan = this.Data.GetSpan(); + this.DataAtPosition = Unsafe.Add(ref MemoryMarshal.GetReference(dataSpan), (int)this.Position); + return true; + } + + return false; + } + private uint WhiteTerminatingCodeRunLength() { switch (this.CurValueBitsRead) @@ -811,13 +836,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { if (this.BitsRead >= 8) { - this.LoadNewByte(); + this.AdvancePosition(); } - Span dataSpan = this.Data.GetSpan(); int shift = 8 - this.BitsRead - 1; - ref byte dataAtPosition = ref Unsafe.Add(ref MemoryMarshal.GetReference(dataSpan), (int)this.Position); - uint bit = (uint)((dataAtPosition & (1 << shift)) != 0 ? 1 : 0); + uint bit = (uint)((this.DataAtPosition & (1 << shift)) != 0 ? 1 : 0); this.BitsRead++; return bit; @@ -827,11 +850,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { this.Position++; this.ResetBitsRead(); - - if (this.Position >= (ulong)this.DataLength) - { - TiffThrowHelper.ThrowImageFormatException("tiff image has invalid ccitt compressed data"); - } } private void ReadImageDataFromStream(Stream input, int bytesToRead) From f0f08e83b497769faf1a97480b9b27fde58e4c61 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 May 2022 09:27:56 +0200 Subject: [PATCH 14/30] Use Numerics.Modulo8 --- .../Formats/Tiff/Compression/BitWriterUtils.cs | 6 +++--- .../ModifiedHuffmanTiffCompression.cs | 12 ++++++------ .../Compression/Decompressors/T4TiffCompression.cs | 6 +++--- .../Compression/Decompressors/T6TiffCompression.cs | 14 +++++++------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs index 7394577a5..2a9efa59b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs @@ -9,12 +9,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { internal static class BitWriterUtils { - public static void WriteBits(Span buffer, int pos, uint count, byte value) + public static void WriteBits(Span buffer, int pos, int count, byte value) { - int bitPos = pos % 8; + int bitPos = Numerics.Modulo8(pos); int bufferPos = pos / 8; int startIdx = bufferPos + bitPos; - int endIdx = (int)(startIdx + count); + int endIdx = startIdx + count; if (value == 1) { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs index 453f7d10d..c45587a72 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors using var bitReader = new ModifiedHuffmanBitReader(stream, this.FillOrder, byteCount, this.Allocator); buffer.Clear(); - uint bitsWritten = 0; + int bitsWritten = 0; uint pixelsWritten = 0; while (bitReader.HasMoreData) { @@ -55,14 +55,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { if (bitReader.IsWhiteRun) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.whiteValue); + BitWriterUtils.WriteBits(buffer, bitsWritten, (int)bitReader.RunLength, this.whiteValue); } else { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.blackValue); + BitWriterUtils.WriteBits(buffer, bitsWritten, (int)bitReader.RunLength, this.blackValue); } - bitsWritten += bitReader.RunLength; + bitsWritten += (int)bitReader.RunLength; pixelsWritten += bitReader.RunLength; } @@ -72,10 +72,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors pixelsWritten = 0; // Write padding bits, if necessary. - uint pad = 8 - (bitsWritten % 8); + int pad = 8 - Numerics.Modulo8(bitsWritten); if (pad != 8) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, pad, 0); + BitWriterUtils.WriteBits(buffer, bitsWritten, pad, 0); bitsWritten += pad; } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs index 158cac947..9bb53e29a 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors uint pad = 8 - (bitsWritten % 8); if (pad != 8) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, pad, 0); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, (int)pad, 0); bitsWritten += pad; } @@ -104,11 +104,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { if (bitReader.IsWhiteRun) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.whiteValue); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, (int)bitReader.RunLength, this.whiteValue); } else { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.blackValue); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, (int)bitReader.RunLength, this.blackValue); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index e9921418d..6a0bb00fc 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors using var bitReader = new T6BitReader(stream, this.FillOrder, byteCount, this.Allocator); var referenceScanLine = new CcittReferenceScanline(this.isWhiteZero, this.width); - uint bitsWritten = 0; + int bitsWritten = 0; for (int y = 0; y < height; y++) { scanLine.Clear(); @@ -73,10 +73,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } } - private uint WriteScanLine(Span buffer, Span scanLine, uint bitsWritten) + private int WriteScanLine(Span buffer, Span scanLine, int bitsWritten) { - int bitPos = (int)(bitsWritten % 8); - int bufferPos = (int)(bitsWritten / 8); + int bitPos = Numerics.Modulo8(bitsWritten); + int bufferPos = bitsWritten / 8; for (nint i = 0; i < scanLine.Length; i++) { if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) != this.white) @@ -95,11 +95,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } // Write padding bytes, if necessary. - uint remainder = bitsWritten % 8; + int remainder = bitsWritten % 8; if (remainder != 0) { - uint padding = 8 - remainder; - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, padding, 0); + int padding = 8 - remainder; + BitWriterUtils.WriteBits(buffer, bitsWritten, padding, 0); bitsWritten += padding; } From 62de4589d54cf3498cc72c1c6a158edbccda7474 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 May 2022 13:38:16 +0200 Subject: [PATCH 15/30] Stream -> BufferedReadStream --- .../Compression/Decompressors/ModifiedHuffmanBitReader.cs | 4 ++-- .../Formats/Tiff/Compression/Decompressors/T4BitReader.cs | 3 ++- .../Formats/Tiff/Compression/Decompressors/T6BitReader.cs | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs index 4a87e2951..de3f3569e 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.IO; using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// The logical order of bits within a byte. /// The number of bytes to read from the stream. /// The memory allocator. - public ModifiedHuffmanBitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) + public ModifiedHuffmanBitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) : base(input, fillOrder, bytesToRead, allocator) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index 1053ca107..89dd7a50e 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -8,6 +8,7 @@ using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors @@ -211,7 +212,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// The number of bytes to read from the stream. /// The memory allocator. /// Indicates, if fill bits have been added as necessary before EOL codes such that EOL always ends on a byte boundary. Defaults to false. - public T4BitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator, bool eolPadding = false) + public T4BitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator, bool eolPadding = false) { this.fillOrder = fillOrder; this.Data = allocator.Allocate(bytesToRead); diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs index e747718a4..160dbd9fc 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs @@ -2,9 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; -using System.IO; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// The logical order of bits within a byte. /// The number of bytes to read from the stream. /// The memory allocator. - public T6BitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) + public T6BitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) : base(input, fillOrder, bytesToRead, allocator) { } From 962466144ae3e4822679be6281dd6f9c637cd845 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 May 2022 19:59:30 +0200 Subject: [PATCH 16/30] Avoid reading all compressed data at once: instead read byte by byte from the stream to avoid allocation --- .../Decompressors/ModifiedHuffmanBitReader.cs | 10 ++- .../ModifiedHuffmanTiffCompression.cs | 12 +++- .../Compression/Decompressors/T4BitReader.cs | 70 +++++++++---------- .../Decompressors/T4TiffCompression.cs | 31 ++++---- .../Compression/Decompressors/T6BitReader.cs | 6 +- .../Decompressors/T6TiffCompression.cs | 2 +- 6 files changed, 69 insertions(+), 62 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs index de3f3569e..8306e91a3 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { @@ -19,14 +18,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// The compressed input stream. /// The logical order of bits within a byte. /// The number of bytes to read from the stream. - /// The memory allocator. - public ModifiedHuffmanBitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) - : base(input, fillOrder, bytesToRead, allocator) + public ModifiedHuffmanBitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead) + : base(input, fillOrder, bytesToRead) { } /// - public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || ((uint)(this.BitsRead - 1) < (7 - 1)); + public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || (uint)(this.BitsRead - 1) < 6; /// public override bool IsEndOfScanLine @@ -53,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { base.StartNewRow(); - int remainder = this.BitsRead & 7; // bit-hack for % 8 + int remainder = Numerics.Modulo8(this.BitsRead); if (remainder != 0) { // Skip padding bits, move to next byte. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs index c45587a72..4ec989742 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs @@ -42,11 +42,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) { - using var bitReader = new ModifiedHuffmanBitReader(stream, this.FillOrder, byteCount, this.Allocator); + var bitReader = new ModifiedHuffmanBitReader(stream, this.FillOrder, byteCount); buffer.Clear(); int bitsWritten = 0; uint pixelsWritten = 0; + nint rowsWritten = 0; while (bitReader.HasMoreData) { bitReader.ReadNextRun(); @@ -68,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors if (pixelsWritten == this.Width) { - bitReader.StartNewRow(); + rowsWritten++; pixelsWritten = 0; // Write padding bits, if necessary. @@ -78,6 +79,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors BitWriterUtils.WriteBits(buffer, bitsWritten, pad, 0); bitsWritten += pad; } + + if (rowsWritten >= stripHeight) + { + break; + } + + bitReader.StartNewRow(); } if (pixelsWritten > this.Width) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index 89dd7a50e..c46066a3a 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -1,22 +1,17 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; using System.Collections.Generic; -using System.IO; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { /// /// Bitreader for reading compressed CCITT T4 1D data. /// - internal class T4BitReader : IDisposable + internal class T4BitReader { /// /// The logical order of bits within a byte. @@ -204,20 +199,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { 0x54, 1408 }, { 0x55, 1472 }, { 0x5A, 1536 }, { 0x5B, 1600 }, { 0x64, 1664 }, { 0x65, 1728 } }; + /// + /// The compressed input stream. + /// + private readonly BufferedReadStream stream; + /// /// Initializes a new instance of the class. /// /// The compressed input stream. /// The logical order of bits within a byte. /// The number of bytes to read from the stream. - /// The memory allocator. /// Indicates, if fill bits have been added as necessary before EOL codes such that EOL always ends on a byte boundary. Defaults to false. - public T4BitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator, bool eolPadding = false) + public T4BitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, bool eolPadding = false) { + this.stream = input; this.fillOrder = fillOrder; - this.Data = allocator.Allocate(bytesToRead); - this.ReadImageDataFromStream(input, bytesToRead); - this.DataLength = bytesToRead; this.BitsRead = 0; this.Value = 0; @@ -230,8 +227,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors this.RunLength = 0; this.eolPadding = eolPadding; - Span dataSpan = this.Data.GetSpan(); - this.DataAtPosition = dataSpan[(int)this.Position]; + this.ReadNextByte(); if (this.eolPadding) { @@ -269,11 +265,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// protected ulong Position { get; set; } - /// - /// Gets the compressed image data. - /// - public IMemoryOwner Data { get; } - /// /// Gets a value indicating whether there is more data to read left. /// @@ -400,9 +391,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors this.terminationCodeFound = false; } - /// - public void Dispose() => this.Data.Dispose(); - /// /// An EOL is expected before the first data. /// @@ -465,14 +453,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// /// Advances the position by one byte. /// - /// True, if data could be advanced by one byte. + /// True, if data could be advanced by one byte, otherwise false. protected bool AdvancePosition() { - this.LoadNewByte(); - if (this.Position < (ulong)this.DataLength) + if (this.LoadNewByte()) { - Span dataSpan = this.Data.GetSpan(); - this.DataAtPosition = Unsafe.Add(ref MemoryMarshal.GetReference(dataSpan), (int)this.Position); return true; } @@ -833,6 +818,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private uint GetBit() { if (this.BitsRead >= 8) @@ -847,24 +833,34 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors return bit; } - private void LoadNewByte() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool LoadNewByte() { + if (this.Position < (ulong)this.DataLength) + { + this.ReadNextByte(); + this.Position++; + return true; + } + this.Position++; - this.ResetBitsRead(); + this.DataAtPosition = 0; + return false; } - private void ReadImageDataFromStream(Stream input, int bytesToRead) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadNextByte() { - Span dataSpan = this.Data.GetSpan(); - input.Read(dataSpan, 0, bytesToRead); - - if (this.fillOrder == TiffFillOrder.LeastSignificantBitFirst) + int nextByte = this.stream.ReadByte(); + if (nextByte == -1) { - for (int i = 0; i < dataSpan.Length; i++) - { - dataSpan[i] = ReverseBits(dataSpan[i]); - } + TiffThrowHelper.ThrowImageFormatException("Tiff fax compression error: not enough data."); } + + this.ResetBitsRead(); + this.DataAtPosition = this.fillOrder == TiffFillOrder.LeastSignificantBitFirst + ? ReverseBits((byte)nextByte) + : (byte)nextByte; } // http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs index 9bb53e29a..254cb2ab0 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs @@ -61,11 +61,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } bool eolPadding = this.faxCompressionOptions.HasFlag(FaxCompressionOptions.EolPadding); - using var bitReader = new T4BitReader(stream, this.FillOrder, byteCount, this.Allocator, eolPadding); + var bitReader = new T4BitReader(stream, this.FillOrder, byteCount, eolPadding); buffer.Clear(); - uint bitsWritten = 0; - uint pixelWritten = 0; + int bitsWritten = 0; + uint pixelsWritten = 0; + nint rowsWritten = 0; while (bitReader.HasMoreData) { bitReader.ReadNextRun(); @@ -74,41 +75,47 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { this.WritePixelRun(buffer, bitReader, bitsWritten); - bitsWritten += bitReader.RunLength; - pixelWritten += bitReader.RunLength; + bitsWritten += (int)bitReader.RunLength; + pixelsWritten += bitReader.RunLength; } if (bitReader.IsEndOfScanLine) { // Write padding bytes, if necessary. - uint pad = 8 - (bitsWritten % 8); + int pad = 8 - Numerics.Modulo8(bitsWritten); if (pad != 8) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, (int)pad, 0); + BitWriterUtils.WriteBits(buffer, bitsWritten, pad, 0); bitsWritten += pad; } - pixelWritten = 0; + pixelsWritten = 0; + rowsWritten++; + + if (rowsWritten >= stripHeight) + { + break; + } } } // Edge case for when we are at the last byte, but there are still some unwritten pixels left. - if (pixelWritten > 0 && pixelWritten < this.width) + if (pixelsWritten > 0 && pixelsWritten < this.width) { bitReader.ReadNextRun(); this.WritePixelRun(buffer, bitReader, bitsWritten); } } - private void WritePixelRun(Span buffer, T4BitReader bitReader, uint bitsWritten) + private void WritePixelRun(Span buffer, T4BitReader bitReader, int bitsWritten) { if (bitReader.IsWhiteRun) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, (int)bitReader.RunLength, this.whiteValue); + BitWriterUtils.WriteBits(buffer, bitsWritten, (int)bitReader.RunLength, this.whiteValue); } else { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, (int)bitReader.RunLength, this.blackValue); + BitWriterUtils.WriteBits(buffer, bitsWritten, (int)bitReader.RunLength, this.blackValue); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs index 160dbd9fc..a3ac0ca2b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { @@ -56,9 +55,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// The compressed input stream. /// The logical order of bits within a byte. /// The number of bytes to read from the stream. - /// The memory allocator. - public T6BitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) - : base(input, fillOrder, bytesToRead, allocator) + public T6BitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead) + : base(input, fillOrder, bytesToRead) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 6a0bb00fc..495fba037 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors Span scanLine = scanLineBuffer.GetSpan().Slice(0, this.width); Span referenceScanLineSpan = scanLineBuffer.GetSpan().Slice(this.width, this.width); - using var bitReader = new T6BitReader(stream, this.FillOrder, byteCount, this.Allocator); + var bitReader = new T6BitReader(stream, this.FillOrder, byteCount); var referenceScanLine = new CcittReferenceScanline(this.isWhiteZero, this.width); int bitsWritten = 0; From 98b96fe0a9610f81549273d922b93f67d13ab242 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 May 2022 20:29:52 +0200 Subject: [PATCH 17/30] Use nint --- src/ImageSharp/Common/Helpers/Numerics.cs | 6 +++++ .../Tiff/Compression/BitWriterUtils.cs | 22 +++++++++---------- .../ModifiedHuffmanTiffCompression.cs | 10 ++++----- .../Decompressors/T4TiffCompression.cs | 10 ++++----- .../Decompressors/T6TiffCompression.cs | 12 +++++----- 5 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index bfbaa1b31..301bef297 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -75,6 +75,12 @@ namespace SixLabors.ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Modulo8(int x) => x & 7; + /// + /// Calculates % 8 + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nint Modulo8(nint x) => x & 7; + /// /// Fast (x mod m) calculator, with the restriction that /// should be power of 2. diff --git a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs index 2a9efa59b..37e2a6efc 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs @@ -9,16 +9,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { internal static class BitWriterUtils { - public static void WriteBits(Span buffer, int pos, int count, byte value) + public static void WriteBits(Span buffer, nint pos, nint count, byte value) { - int bitPos = Numerics.Modulo8(pos); - int bufferPos = pos / 8; - int startIdx = bufferPos + bitPos; - int endIdx = startIdx + count; + nint bitPos = Numerics.Modulo8(pos); + nint bufferPos = pos / 8; + nint startIdx = bufferPos + bitPos; + nint endIdx = startIdx + count; if (value == 1) { - for (int i = startIdx; i < endIdx; i++) + for (nint i = startIdx; i < endIdx; i++) { WriteBit(buffer, bufferPos, bitPos); @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } else { - for (int i = startIdx; i < endIdx; i++) + for (nint i = startIdx; i < endIdx; i++) { WriteZeroBit(buffer, bufferPos, bitPos); @@ -47,17 +47,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } [MethodImpl(InliningOptions.ShortMethod)] - public static void WriteBit(Span buffer, int bufferPos, int bitPos) + public static void WriteBit(Span buffer, nint bufferPos, nint bitPos) { ref byte b = ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), bufferPos); - b |= (byte)(1 << (7 - bitPos)); + b |= (byte)(1 << (int)(7 - bitPos)); } [MethodImpl(InliningOptions.ShortMethod)] - public static void WriteZeroBit(Span buffer, int bufferPos, int bitPos) + public static void WriteZeroBit(Span buffer, nint bufferPos, nint bitPos) { ref byte b = ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), bufferPos); - b = (byte)(b & ~(1 << (7 - bitPos))); + b = (byte)(b & ~(1 << (int)(7 - bitPos))); } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs index 4ec989742..54c94525c 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs @@ -45,8 +45,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors var bitReader = new ModifiedHuffmanBitReader(stream, this.FillOrder, byteCount); buffer.Clear(); - int bitsWritten = 0; - uint pixelsWritten = 0; + nint bitsWritten = 0; + nuint pixelsWritten = 0; nint rowsWritten = 0; while (bitReader.HasMoreData) { @@ -67,13 +67,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors pixelsWritten += bitReader.RunLength; } - if (pixelsWritten == this.Width) + if (pixelsWritten == (ulong)this.Width) { rowsWritten++; pixelsWritten = 0; // Write padding bits, if necessary. - int pad = 8 - Numerics.Modulo8(bitsWritten); + nint pad = 8 - Numerics.Modulo8(bitsWritten); if (pad != 8) { BitWriterUtils.WriteBits(buffer, bitsWritten, pad, 0); @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors bitReader.StartNewRow(); } - if (pixelsWritten > this.Width) + if (pixelsWritten > (ulong)this.Width) { TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, decoded more pixels then image width"); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs index 254cb2ab0..7b59e7117 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs @@ -64,8 +64,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors var bitReader = new T4BitReader(stream, this.FillOrder, byteCount, eolPadding); buffer.Clear(); - int bitsWritten = 0; - uint pixelsWritten = 0; + nint bitsWritten = 0; + nuint pixelsWritten = 0; nint rowsWritten = 0; while (bitReader.HasMoreData) { @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors if (bitReader.IsEndOfScanLine) { // Write padding bytes, if necessary. - int pad = 8 - Numerics.Modulo8(bitsWritten); + nint pad = 8 - Numerics.Modulo8(bitsWritten); if (pad != 8) { BitWriterUtils.WriteBits(buffer, bitsWritten, pad, 0); @@ -100,14 +100,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } // Edge case for when we are at the last byte, but there are still some unwritten pixels left. - if (pixelsWritten > 0 && pixelsWritten < this.width) + if (pixelsWritten > 0 && pixelsWritten < (ulong)this.width) { bitReader.ReadNextRun(); this.WritePixelRun(buffer, bitReader, bitsWritten); } } - private void WritePixelRun(Span buffer, T4BitReader bitReader, int bitsWritten) + private void WritePixelRun(Span buffer, T4BitReader bitReader, nint bitsWritten) { if (bitReader.IsWhiteRun) { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 495fba037..890eecbff 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors var bitReader = new T6BitReader(stream, this.FillOrder, byteCount); var referenceScanLine = new CcittReferenceScanline(this.isWhiteZero, this.width); - int bitsWritten = 0; + nint bitsWritten = 0; for (int y = 0; y < height; y++) { scanLine.Clear(); @@ -73,10 +73,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } } - private int WriteScanLine(Span buffer, Span scanLine, int bitsWritten) + private nint WriteScanLine(Span buffer, Span scanLine, nint bitsWritten) { - int bitPos = Numerics.Modulo8(bitsWritten); - int bufferPos = bitsWritten / 8; + nint bitPos = Numerics.Modulo8(bitsWritten); + nint bufferPos = bitsWritten / 8; for (nint i = 0; i < scanLine.Length; i++) { if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) != this.white) @@ -95,10 +95,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } // Write padding bytes, if necessary. - int remainder = bitsWritten % 8; + nint remainder = Numerics.Modulo8(bitsWritten); if (remainder != 0) { - int padding = 8 - remainder; + nint padding = 8 - remainder; BitWriterUtils.WriteBits(buffer, bitsWritten, padding, 0); bitsWritten += padding; } From d5a3de1184f3efe8befa2e9ac1ac62c39025b235 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 May 2022 21:43:28 +0200 Subject: [PATCH 18/30] Avoid using Dictionary's --- .../Decompressors/CcittTwoDimensionalCode.cs | 12 ++- .../Compression/Decompressors/T6BitReader.cs | 98 +++++++++++-------- .../Decompressors/T6TiffCompression.cs | 3 +- 3 files changed, 70 insertions(+), 43 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittTwoDimensionalCode.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittTwoDimensionalCode.cs index 74a17b907..9543499d7 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittTwoDimensionalCode.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittTwoDimensionalCode.cs @@ -13,15 +13,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// /// Initializes a new instance of the struct. /// - /// The type. + /// The code word. + /// The type of the code. /// The bits required. /// The extension bits. - public CcittTwoDimensionalCode(CcittTwoDimensionalCodeType type, int bitsRequired, int extensionBits = 0) - => this.value = (ushort)((byte)type | ((bitsRequired & 0b1111) << 8) | ((extensionBits & 0b111) << 11)); + public CcittTwoDimensionalCode(int code, CcittTwoDimensionalCodeType type, int bitsRequired, int extensionBits = 0) + { + this.Code = code; + this.value = (ushort)((byte)type | ((bitsRequired & 0b1111) << 8) | ((extensionBits & 0b111) << 11)); + } /// /// Gets the code type. /// public CcittTwoDimensionalCodeType Type => (CcittTwoDimensionalCodeType)(this.value & 0b11111111); + + public int Code { get; } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs index a3ac0ca2b..0d068bb6f 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; @@ -16,38 +15,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { private readonly int maxCodeLength = 12; - private static readonly CcittTwoDimensionalCode None = new(CcittTwoDimensionalCodeType.None, 0); + private static readonly CcittTwoDimensionalCode None = new(0, CcittTwoDimensionalCodeType.None, 0); - private static readonly Dictionary Len1Codes = new() - { - { 0b1, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Vertical0, 1) } - }; + private static readonly CcittTwoDimensionalCode Len1Code1 = new(0b1, CcittTwoDimensionalCodeType.Vertical0, 1); - private static readonly Dictionary Len3Codes = new() - { - { 0b001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Horizontal, 3) }, - { 0b010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL1, 3) }, - { 0b011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR1, 3) } - }; + private static readonly CcittTwoDimensionalCode Len3Code001 = new(0b001, CcittTwoDimensionalCodeType.Horizontal, 3); + private static readonly CcittTwoDimensionalCode Len3Code010 = new(0b010, CcittTwoDimensionalCodeType.VerticalL1, 3); + private static readonly CcittTwoDimensionalCode Len3Code011 = new(0b011, CcittTwoDimensionalCodeType.VerticalR1, 3); - private static readonly Dictionary Len4Codes = new() - { - { 0b0001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Pass, 4) } - }; + private static readonly CcittTwoDimensionalCode Len4Code0001 = new(0b0001, CcittTwoDimensionalCodeType.Pass, 4); - private static readonly Dictionary Len6Codes = new() - { - { 0b000011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR2, 6) }, - { 0b000010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL2, 6) } - }; + private static readonly CcittTwoDimensionalCode Len6Code000011 = new(0b000011, CcittTwoDimensionalCodeType.VerticalR2, 6); + private static readonly CcittTwoDimensionalCode Len6Code000010 = new(0b000010, CcittTwoDimensionalCodeType.VerticalL2, 6); - private static readonly Dictionary Len7Codes = new() - { - { 0b0000011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR3, 7) }, - { 0b0000010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL3, 7) }, - { 0b0000001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Extensions2D, 7) }, - { 0b0000000, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Extensions1D, 7) } - }; + private static readonly CcittTwoDimensionalCode Len7Code0000011 = new(0b0000011, CcittTwoDimensionalCodeType.VerticalR3, 7); + private static readonly CcittTwoDimensionalCode Len7Code0000010 = new(0b0000010, CcittTwoDimensionalCodeType.VerticalL3, 7); + private static readonly CcittTwoDimensionalCode Len7Code0000001 = new(0b0000001, CcittTwoDimensionalCodeType.Extensions2D, 7); + private static readonly CcittTwoDimensionalCode Len7Code0000000 = new(0b0000000, CcittTwoDimensionalCodeType.Extensions1D, 7); /// /// Initializes a new instance of the class. @@ -61,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } /// - public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || ((uint)(this.BitsRead - 1) < (7 - 1)); + public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || (uint)(this.BitsRead - 1) < (7 - 1); /// /// Gets or sets the two dimensional code. @@ -84,45 +68,81 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors switch (this.CurValueBitsRead) { case 1: - if (Len1Codes.ContainsKey(value)) + if (value == Len1Code1.Code) { - this.Code = Len1Codes[value]; + this.Code = Len1Code1; return false; } break; case 3: - if (Len3Codes.ContainsKey(value)) + if (value == Len3Code001.Code) { - this.Code = Len3Codes[value]; + this.Code = Len3Code001; + return false; + } + + if (value == Len3Code010.Code) + { + this.Code = Len3Code010; + return false; + } + + if (value == Len3Code011.Code) + { + this.Code = Len3Code011; return false; } break; case 4: - if (Len4Codes.ContainsKey(value)) + if (value == Len4Code0001.Code) { - this.Code = Len4Codes[value]; + this.Code = Len4Code0001; return false; } break; case 6: - if (Len6Codes.ContainsKey(value)) + if (value == Len6Code000010.Code) { - this.Code = Len6Codes[value]; + this.Code = Len6Code000010; + return false; + } + + if (value == Len6Code000011.Code) + { + this.Code = Len6Code000011; return false; } break; case 7: - if (Len7Codes.ContainsKey(value)) + if (value == Len7Code0000000.Code) + { + this.Code = Len7Code0000000; + return false; + } + + if (value == Len7Code0000001.Code) + { + this.Code = Len7Code0000001; + return false; + } + + if (value == Len7Code0000011.Code) + { + this.Code = Len7Code0000011; + return false; + } + + if (value == Len7Code0000010.Code) { - this.Code = Len7Codes[value]; + this.Code = Len7Code0000010; return false; } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 890eecbff..7cfcfe12b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -77,9 +77,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { nint bitPos = Numerics.Modulo8(bitsWritten); nint bufferPos = bitsWritten / 8; + ref byte scanLineRef = ref MemoryMarshal.GetReference(scanLine); for (nint i = 0; i < scanLine.Length; i++) { - if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) != this.white) + if (Unsafe.Add(ref scanLineRef, i) != this.white) { BitWriterUtils.WriteBit(buffer, bufferPos, bitPos); } From 188b5cd2c66a45e9b4b5eb5a345227a64f9080cb Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 May 2022 10:24:09 +0200 Subject: [PATCH 19/30] Use more nint --- .../BlackIsZero1TiffColor{TPixel}.cs | 10 +++++----- .../WhiteIsZero1TiffColor{TPixel}.cs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs index 2d8e21d6c..dd4cf873b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs @@ -19,21 +19,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - int offset = 0; + nint offset = 0; var colorBlack = default(TPixel); var colorWhite = default(TPixel); colorBlack.FromRgba32(Color.Black); colorWhite.FromRgba32(Color.White); ref byte dataRef = ref MemoryMarshal.GetReference(data); - for (int y = top; y < top + height; y++) + for (nint y = top; y < top + height; y++) { - Span pixelRowSpan = pixels.DangerousGetRowSpan(y); + Span pixelRowSpan = pixels.DangerousGetRowSpan((int)y); ref TPixel pixelRowRef = ref MemoryMarshal.GetReference(pixelRowSpan); - for (int x = left; x < left + width; x += 8) + for (nint x = left; x < left + width; x += 8) { byte b = Unsafe.Add(ref dataRef, offset++); - int maxShift = Math.Min(left + width - x, 8); + nint maxShift = Math.Min(left + width - x, 8); for (int shift = 0; shift < maxShift; shift++) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs index ad7d6c08a..ed8548d41 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs @@ -18,21 +18,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - int offset = 0; + nint offset = 0; var colorBlack = default(TPixel); var colorWhite = default(TPixel); colorBlack.FromRgba32(Color.Black); colorWhite.FromRgba32(Color.White); ref byte dataRef = ref MemoryMarshal.GetReference(data); - for (int y = top; y < top + height; y++) + for (nint y = top; y < top + height; y++) { - Span pixelRowSpan = pixels.DangerousGetRowSpan(y); + Span pixelRowSpan = pixels.DangerousGetRowSpan((int)y); ref TPixel pixelRowRef = ref MemoryMarshal.GetReference(pixelRowSpan); - for (int x = left; x < left + width; x += 8) + for (nint x = left; x < left + width; x += 8) { byte b = Unsafe.Add(ref dataRef, offset++); - int maxShift = Math.Min(left + width - x, 8); + nint maxShift = Math.Min(left + width - x, 8); for (int shift = 0; shift < maxShift; shift++) { From 29b4647cf0f24e7de620bbf1e965e772f330997d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 May 2022 14:16:51 +0200 Subject: [PATCH 20/30] Remove no longer needed Vector4.Zero workaround for netcore2.1 --- .../BlackIsZero16TiffColor{TPixel}.cs | 5 ++--- .../BlackIsZero24TiffColor{TPixel}.cs | 3 ++- .../BlackIsZero32FloatTiffColor{TPixel}.cs | 5 +---- .../BlackIsZero32TiffColor{TPixel}.cs | 5 ++--- .../PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs | 5 ++--- .../Rgb16PlanarTiffColor{TPixel}.cs | 5 ++--- .../PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs | 5 ++--- .../Rgb24PlanarTiffColor{TPixel}.cs | 5 ++--- .../PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs | 5 ++--- .../Rgb32PlanarTiffColor{TPixel}.cs | 5 ++--- .../RgbFloat323232TiffColor{TPixel}.cs | 2 +- .../Rgba16161616TiffColor{TPixel}.cs | 4 +--- .../Rgba16PlanarTiffColor{TPixel}.cs | 5 ++--- .../Rgba24242424TiffColor{TPixel}.cs | 5 ++--- .../Rgba24PlanarTiffColor{TPixel}.cs | 5 ++--- .../Rgba32323232TiffColor{TPixel}.cs | 5 ++--- .../Rgba32PlanarTiffColor{TPixel}.cs | 5 ++--- .../PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs | 2 +- .../RgbaFloat32323232TiffColor{TPixel}.cs | 2 +- .../WhiteIsZero16TiffColor{TPixel}.cs | 3 ++- .../WhiteIsZero24TiffColor{TPixel}.cs | 3 ++- .../WhiteIsZero32FloatTiffColor{TPixel}.cs | 2 +- .../WhiteIsZero32TiffColor{TPixel}.cs | 3 ++- src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs | 2 -- 24 files changed, 40 insertions(+), 56 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs index e60562912..6f3189e70 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -32,11 +33,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 L16 l16 = TiffUtils.L16Default; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs index 7d230dfd5..1c942fa9b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs index c43b121ca..fc526a86f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs @@ -3,7 +3,6 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -26,10 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs index 00e4caef7..b2ab12700 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -25,10 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs index 609369011..0527eaaf0 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -32,11 +33,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 Rgba64 rgba = TiffUtils.Rgba64Default; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs index 76fed3c93..fc5dc82aa 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -26,11 +27,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 Rgba64 rgba = TiffUtils.Rgba64Default; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); Span redData = data[0].GetSpan(); Span greenData = data[1].GetSpan(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs index addf576e9..4fc25f2dd 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -25,10 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs index 2eda3b5af..59bc94802 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -26,10 +27,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs index 02319bfa6..b8f9da72f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -25,10 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs index 26f75bfcf..8903b8a40 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -26,10 +27,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); Span redData = data[0].GetSpan(); Span greenData = data[1].GetSpan(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs index 7fd98dd50..c2590286b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; byte[] buffer = new byte[4]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs index 0340438cb..d1164b67a 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs @@ -42,11 +42,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 Rgba64 rgba = TiffUtils.Rgba64Default; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs index 856d810d3..51e6e21df 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -33,11 +34,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 Rgba64 rgba = TiffUtils.Rgba64Default; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); Span redData = data[0].GetSpan(); Span greenData = data[1].GetSpan(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs index 2ce30252f..ff35a8d0d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -32,10 +33,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs index 89172cfe7..1a3459fcb 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -33,10 +34,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs index 8ee9eb0bf..378338de6 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -32,10 +33,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs index c98ac1cf0..af3a888a7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -33,10 +34,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); Span redData = data[0].GetSpan(); Span greenData = data[1].GetSpan(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs index 967a68ad0..24cf8019c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); using IMemoryOwner vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate(width) : null; Span vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : Span.Empty; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs index f95045ec5..f27348586 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; byte[] buffer = new byte[4]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs index d509776d7..02e95b542 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -29,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 L16 l16 = TiffUtils.L16Default; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs index fbf813078..af6660979 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; const uint maxValue = 0xFFFFFF; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs index 40d1541c5..59017a6c0 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs index fd908c1e9..1d7ac881c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); const uint maxValue = 0xFFFFFFFF; int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index 532423c4f..2f1ddcf21 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -18,8 +18,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils private const float Scale32Bit = 1.0f / 0xFFFFFFFF; - public static Vector4 Vector4Default { get; } = new(0.0f, 0.0f, 0.0f, 0.0f); - public static Rgba64 Rgba64Default { get; } = new(0, 0, 0, 0); public static L16 L16Default { get; } = new(0); From affb1430f23cc1b13e7f5ddc607f5a74ddc0d76c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 May 2022 14:53:30 +0200 Subject: [PATCH 21/30] Remove no longer valid comments --- .../PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs | 2 -- .../RgbFloat323232TiffColor{TPixel}.cs | 2 -- .../PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs | 2 -- .../PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs | 2 -- .../WhiteIsZero32FloatTiffColor{TPixel}.cs | 2 -- .../PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs | 2 -- 6 files changed, 12 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs index 1c942fa9b..f38470526 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs index c2590286b..2aa810623 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs index 02e95b542..092ba68e2 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 L16 l16 = TiffUtils.L16Default; var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs index af6660979..73c168a69 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs index 59017a6c0..f3b72edb5 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs index 1d7ac881c..48e24deda 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); const uint maxValue = 0xFFFFFFFF; From 10ff24fa8754981d80462fa99d00117b05550bb0 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 May 2022 17:14:52 +0200 Subject: [PATCH 22/30] Loop unroll --- .../BlackIsZero1TiffColor{TPixel}.cs | 45 +++++++++++++++++-- .../WhiteIsZero1TiffColor{TPixel}.cs | 45 +++++++++++++++++-- 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs index dd4cf873b..cf8f8fb73 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs @@ -35,12 +35,49 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation byte b = Unsafe.Add(ref dataRef, offset++); nint maxShift = Math.Min(left + width - x, 8); - for (int shift = 0; shift < maxShift; shift++) + if (maxShift == 8) { - int bit = (b >> (7 - shift)) & 1; + int bit = (b >> 7) & 1; + ref TPixel pixel0 = ref Unsafe.Add(ref pixelRowRef, x); + pixel0 = bit == 0 ? colorBlack : colorWhite; - ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); - pixel = bit == 0 ? colorBlack : colorWhite; + bit = (b >> 6) & 1; + ref TPixel pixel1 = ref Unsafe.Add(ref pixelRowRef, x + 1); + pixel1 = bit == 0 ? colorBlack : colorWhite; + + bit = (b >> 5) & 1; + ref TPixel pixel2 = ref Unsafe.Add(ref pixelRowRef, x + 2); + pixel2 = bit == 0 ? colorBlack : colorWhite; + + bit = (b >> 4) & 1; + ref TPixel pixel3 = ref Unsafe.Add(ref pixelRowRef, x + 3); + pixel3 = bit == 0 ? colorBlack : colorWhite; + + bit = (b >> 3) & 1; + ref TPixel pixel4 = ref Unsafe.Add(ref pixelRowRef, x + 4); + pixel4 = bit == 0 ? colorBlack : colorWhite; + + bit = (b >> 2) & 1; + ref TPixel pixel5 = ref Unsafe.Add(ref pixelRowRef, x + 5); + pixel5 = bit == 0 ? colorBlack : colorWhite; + + bit = (b >> 1) & 1; + ref TPixel pixel6 = ref Unsafe.Add(ref pixelRowRef, x + 6); + pixel6 = bit == 0 ? colorBlack : colorWhite; + + bit = b & 1; + ref TPixel pixel7 = ref Unsafe.Add(ref pixelRowRef, x + 7); + pixel7 = bit == 0 ? colorBlack : colorWhite; + } + else + { + for (int shift = 0; shift < maxShift; shift++) + { + int bit = (b >> (7 - shift)) & 1; + + ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); + pixel = bit == 0 ? colorBlack : colorWhite; + } } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs index ed8548d41..e3e95d9e1 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs @@ -34,12 +34,49 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation byte b = Unsafe.Add(ref dataRef, offset++); nint maxShift = Math.Min(left + width - x, 8); - for (int shift = 0; shift < maxShift; shift++) + if (maxShift == 8) { - int bit = (b >> (7 - shift)) & 1; + int bit = (b >> 7) & 1; + ref TPixel pixel0 = ref Unsafe.Add(ref pixelRowRef, x); + pixel0 = bit == 0 ? colorWhite : colorBlack; - ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); - pixel = bit == 0 ? colorWhite : colorBlack; + bit = (b >> 6) & 1; + ref TPixel pixel1 = ref Unsafe.Add(ref pixelRowRef, x + 1); + pixel1 = bit == 0 ? colorWhite : colorBlack; + + bit = (b >> 5) & 1; + ref TPixel pixel2 = ref Unsafe.Add(ref pixelRowRef, x + 2); + pixel2 = bit == 0 ? colorWhite : colorBlack; + + bit = (b >> 4) & 1; + ref TPixel pixel3 = ref Unsafe.Add(ref pixelRowRef, x + 3); + pixel3 = bit == 0 ? colorWhite : colorBlack; + + bit = (b >> 3) & 1; + ref TPixel pixel4 = ref Unsafe.Add(ref pixelRowRef, x + 4); + pixel4 = bit == 0 ? colorWhite : colorBlack; + + bit = (b >> 2) & 1; + ref TPixel pixel5 = ref Unsafe.Add(ref pixelRowRef, x + 5); + pixel5 = bit == 0 ? colorWhite : colorBlack; + + bit = (b >> 1) & 1; + ref TPixel pixel6 = ref Unsafe.Add(ref pixelRowRef, x + 6); + pixel6 = bit == 0 ? colorWhite : colorBlack; + + bit = b & 1; + ref TPixel pixel7 = ref Unsafe.Add(ref pixelRowRef, x + 7); + pixel7 = bit == 0 ? colorWhite : colorBlack; + } + else + { + for (int shift = 0; shift < maxShift; shift++) + { + int bit = (b >> (7 - shift)) & 1; + + ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); + pixel = bit == 0 ? colorWhite : colorBlack; + } } } } From 50c1aab54fef01b1e21da4cbb156a6a9da4a438c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 May 2022 20:22:56 +0200 Subject: [PATCH 23/30] Use stackalloc --- .../Formats/Tiff/Compression/Decompressors/T4BitReader.cs | 1 + .../BlackIsZero24TiffColor{TPixel}.cs | 4 ++-- .../WhiteIsZero24TiffColor{TPixel}.cs | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index c46066a3a..226bfe5da 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -434,6 +434,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// /// The number of bits to read. /// The value read. + [MethodImpl(InliningOptions.ShortMethod)] protected uint ReadValue(int nBits) { DebugGuard.MustBeGreaterThan(nBits, 0, nameof(nBits)); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs index f38470526..9be8dd774 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs @@ -28,10 +28,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); - byte[] buffer = new byte[4]; + Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; - Span bufferSpan = buffer.AsSpan(bufferStartIdx); + Span bufferSpan = buffer.Slice(bufferStartIdx); int offset = 0; for (int y = top; y < top + height; y++) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs index 73c168a69..d483d7faf 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs @@ -28,11 +28,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); - byte[] buffer = new byte[4]; + Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; const uint maxValue = 0xFFFFFF; - Span bufferSpan = buffer.AsSpan(bufferStartIdx); + Span bufferSpan = buffer.Slice(bufferStartIdx); int offset = 0; for (int y = top; y < top + height; y++) { From 8ff29a1a3fd9fbef47153ea3bdd42d471ecc482b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 1 Jun 2022 22:54:30 +0200 Subject: [PATCH 24/30] PackBitsTiffCompression: Use Slice.Fill to repeat the data --- .../Decompressors/PackBitsTiffCompression.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs index 4093d8987..d7bba88fd 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors byte repeatData = compressedData[compressedOffset + 1]; int repeatLength = 257 - headerByte; - ArrayCopyRepeat(repeatData, buffer, decompressedOffset, repeatLength); + buffer.Slice(decompressedOffset, repeatLength).Fill(repeatData); compressedOffset += 2; decompressedOffset += repeatLength; @@ -81,14 +81,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } } - private static void ArrayCopyRepeat(byte value, Span destinationArray, int destinationIndex, int length) - { - for (int i = 0; i < length; i++) - { - destinationArray[i + destinationIndex] = value; - } - } - /// protected override void Dispose(bool disposing) => this.compressedDataMemory?.Dispose(); } From 7434643eeed5493e6d2aab23751d8d1626fa2d77 Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Thu, 2 Jun 2022 00:37:09 +0200 Subject: [PATCH 25/30] Remove no longer valid comment Co-authored-by: Berkan Diler --- .../RgbaFloat32323232TiffColor{TPixel}.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs index f27348586..317c2db3b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); int offset = 0; From 1b7f800c074deebb62d580d203b228ef235874a0 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 2 Jun 2022 11:45:54 +0200 Subject: [PATCH 26/30] Minor: Another instance of use Numerics.Modulo8 instead of % 8 --- .../Tiff/Compression/Compressors/TiffCcittCompressor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs index 316610621..052b01f27 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs @@ -442,16 +442,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors } /// - /// Pads output to the next byte + /// Pads output to the next byte. /// /// /// If the output is not currently on a byte boundary, - /// zero-pad it to the next byte + /// zero-pad it to the next byte. /// protected void PadByte() { // Check if padding is necessary. - if (this.bitPosition % 8 != 0) + if (Numerics.Modulo8(this.bitPosition) != 0) { // Skip padding bits, move to next byte. this.bytePosition++; From 34584b9b3f2f3732bc1cd7cc4c11b8be27cff265 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 2 Jun 2022 12:07:56 +0200 Subject: [PATCH 27/30] Add another test case for Fax4 compressed with min is black --- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 2 ++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Tiff/CCITTGroup4_minisblack.tiff | 3 +++ 3 files changed, 6 insertions(+) create mode 100644 tests/Images/Input/Tiff/CCITTGroup4_minisblack.tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index b9569c7a4..7987d76a3 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -611,6 +611,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Fax4Compressed, PixelTypes.Rgba32)] + [WithFile(Fax4Compressed2, PixelTypes.Rgba32)] + [WithFile(Fax4CompressedMinIsBlack, PixelTypes.Rgba32)] [WithFile(Fax4CompressedLowerOrderBitsFirst, PixelTypes.Rgba32)] [WithFile(Calliphora_Fax4Compressed, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_Fax4Compressed(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 1f6da39b2..ace20c2e4 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -762,6 +762,7 @@ namespace SixLabors.ImageSharp.Tests public const string Fax4Compressed = "Tiff/basi3p02_fax4.tiff"; public const string Fax4Compressed2 = "Tiff/CCITTGroup4.tiff"; public const string Fax4CompressedLowerOrderBitsFirst = "Tiff/basi3p02_fax4_lowerOrderBitsFirst.tiff"; + public const string Fax4CompressedMinIsBlack = "Tiff/CCITTGroup4_minisblack.tiff"; public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tiff"; public const string CcittFax3AllMakeupCodes = "Tiff/ccitt_fax3_all_makeup_codes.tiff"; diff --git a/tests/Images/Input/Tiff/CCITTGroup4_minisblack.tiff b/tests/Images/Input/Tiff/CCITTGroup4_minisblack.tiff new file mode 100644 index 000000000..8ae464753 --- /dev/null +++ b/tests/Images/Input/Tiff/CCITTGroup4_minisblack.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c12c96059a6214739fe836572bce6912dcf4a0d2ff389c840f0d2daa4465f55 +size 11637 From 035c31e826d8e0d7199e9124bae46c668fd4eda5 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 3 Jun 2022 15:31:16 +0200 Subject: [PATCH 28/30] Make sure 1 Bit compression is only used with 1 bit pixel type --- .../Compressors/TiffCcittCompressor.cs | 54 +++++++++---------- .../Formats/Tiff/TiffEncoderCore.cs | 31 +++++++---- .../Formats/Tiff/TiffEncoderTests.cs | 22 ++++++++ 3 files changed, 71 insertions(+), 36 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs index 052b01f27..f92cf1822 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs @@ -23,28 +23,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors 64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 1792, 1856, 1920, 1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560 }; - private static readonly Dictionary WhiteLen4TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen4TermCodes = new() { { 2, 0x7 }, { 3, 0x8 }, { 4, 0xB }, { 5, 0xC }, { 6, 0xE }, { 7, 0xF } }; - private static readonly Dictionary WhiteLen5TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen5TermCodes = new() { { 8, 0x13 }, { 9, 0x14 }, { 10, 0x7 }, { 11, 0x8 } }; - private static readonly Dictionary WhiteLen6TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen6TermCodes = new() { { 1, 0x7 }, { 12, 0x8 }, { 13, 0x3 }, { 14, 0x34 }, { 15, 0x35 }, { 16, 0x2A }, { 17, 0x2B } }; - private static readonly Dictionary WhiteLen7TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen7TermCodes = new() { { 18, 0x27 }, { 19, 0xC }, { 20, 0x8 }, { 21, 0x17 }, { 22, 0x3 }, { 23, 0x4 }, { 24, 0x28 }, { 25, 0x2B }, { 26, 0x13 }, { 27, 0x24 }, { 28, 0x18 } }; - private static readonly Dictionary WhiteLen8TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen8TermCodes = new() { { 0, WhiteZeroRunTermCode }, { 29, 0x2 }, { 30, 0x3 }, { 31, 0x1A }, { 32, 0x1B }, { 33, 0x12 }, { 34, 0x13 }, { 35, 0x14 }, { 36, 0x15 }, { 37, 0x16 }, { 38, 0x17 }, { 39, 0x28 }, { 40, 0x29 }, { 41, 0x2A }, { 42, 0x2B }, { 43, 0x2C }, { 44, 0x2D }, @@ -53,57 +53,57 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { 63, 0x34 } }; - private static readonly Dictionary BlackLen2TermCodes = new Dictionary() + private static readonly Dictionary BlackLen2TermCodes = new() { { 2, 0x3 }, { 3, 0x2 } }; - private static readonly Dictionary BlackLen3TermCodes = new Dictionary() + private static readonly Dictionary BlackLen3TermCodes = new() { { 1, 0x2 }, { 4, 0x3 } }; - private static readonly Dictionary BlackLen4TermCodes = new Dictionary() + private static readonly Dictionary BlackLen4TermCodes = new() { { 5, 0x3 }, { 6, 0x2 } }; - private static readonly Dictionary BlackLen5TermCodes = new Dictionary() + private static readonly Dictionary BlackLen5TermCodes = new() { { 7, 0x3 } }; - private static readonly Dictionary BlackLen6TermCodes = new Dictionary() + private static readonly Dictionary BlackLen6TermCodes = new() { { 8, 0x5 }, { 9, 0x4 } }; - private static readonly Dictionary BlackLen7TermCodes = new Dictionary() + private static readonly Dictionary BlackLen7TermCodes = new() { { 10, 0x4 }, { 11, 0x5 }, { 12, 0x7 } }; - private static readonly Dictionary BlackLen8TermCodes = new Dictionary() + private static readonly Dictionary BlackLen8TermCodes = new() { { 13, 0x4 }, { 14, 0x7 } }; - private static readonly Dictionary BlackLen9TermCodes = new Dictionary() + private static readonly Dictionary BlackLen9TermCodes = new() { { 15, 0x18 } }; - private static readonly Dictionary BlackLen10TermCodes = new Dictionary() + private static readonly Dictionary BlackLen10TermCodes = new() { { 0, BlackZeroRunTermCode }, { 16, 0x17 }, { 17, 0x18 }, { 18, 0x8 } }; - private static readonly Dictionary BlackLen11TermCodes = new Dictionary() + private static readonly Dictionary BlackLen11TermCodes = new() { { 19, 0x67 }, { 20, 0x68 }, { 21, 0x6C }, { 22, 0x37 }, { 23, 0x28 }, { 24, 0x17 }, { 25, 0x18 } }; - private static readonly Dictionary BlackLen12TermCodes = new Dictionary() + private static readonly Dictionary BlackLen12TermCodes = new() { { 26, 0xCA }, { 27, 0xCB }, { 28, 0xCC }, { 29, 0xCD }, { 30, 0x68 }, { 31, 0x69 }, { 32, 0x6A }, { 33, 0x6B }, { 34, 0xD2 }, { 35, 0xD3 }, { 36, 0xD4 }, { 37, 0xD5 }, { 38, 0xD6 }, { 39, 0xD7 }, { 40, 0x6C }, { 41, 0x6D }, { 42, 0xDA }, { 43, 0xDB }, @@ -112,62 +112,62 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { 62, 0x66 }, { 63, 0x67 } }; - private static readonly Dictionary WhiteLen5MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen5MakeupCodes = new() { { 64, 0x1B }, { 128, 0x12 } }; - private static readonly Dictionary WhiteLen6MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen6MakeupCodes = new() { { 192, 0x17 }, { 1664, 0x18 } }; - private static readonly Dictionary WhiteLen8MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen8MakeupCodes = new() { { 320, 0x36 }, { 384, 0x37 }, { 448, 0x64 }, { 512, 0x65 }, { 576, 0x68 }, { 640, 0x67 } }; - private static readonly Dictionary WhiteLen7MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen7MakeupCodes = new() { { 256, 0x37 } }; - private static readonly Dictionary WhiteLen9MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen9MakeupCodes = new() { { 704, 0xCC }, { 768, 0xCD }, { 832, 0xD2 }, { 896, 0xD3 }, { 960, 0xD4 }, { 1024, 0xD5 }, { 1088, 0xD6 }, { 1152, 0xD7 }, { 1216, 0xD8 }, { 1280, 0xD9 }, { 1344, 0xDA }, { 1408, 0xDB }, { 1472, 0x98 }, { 1536, 0x99 }, { 1600, 0x9A }, { 1728, 0x9B } }; - private static readonly Dictionary WhiteLen11MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen11MakeupCodes = new() { { 1792, 0x8 }, { 1856, 0xC }, { 1920, 0xD } }; - private static readonly Dictionary WhiteLen12MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen12MakeupCodes = new() { { 1984, 0x12 }, { 2048, 0x13 }, { 2112, 0x14 }, { 2176, 0x15 }, { 2240, 0x16 }, { 2304, 0x17 }, { 2368, 0x1C }, { 2432, 0x1D }, { 2496, 0x1E }, { 2560, 0x1F } }; - private static readonly Dictionary BlackLen10MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen10MakeupCodes = new() { { 64, 0xF } }; - private static readonly Dictionary BlackLen11MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen11MakeupCodes = new() { { 1792, 0x8 }, { 1856, 0xC }, { 1920, 0xD } }; - private static readonly Dictionary BlackLen12MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen12MakeupCodes = new() { { 128, 0xC8 }, { 192, 0xC9 }, { 256, 0x5B }, { 320, 0x33 }, { 384, 0x34 }, { 448, 0x35 }, { 1984, 0x12 }, { 2048, 0x13 }, { 2112, 0x14 }, { 2176, 0x15 }, { 2240, 0x16 }, { 2304, 0x17 }, { 2368, 0x1C }, { 2432, 0x1D }, { 2496, 0x1E }, { 2560, 0x1F } }; - private static readonly Dictionary BlackLen13MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen13MakeupCodes = new() { { 512, 0x6C }, { 576, 0x6D }, { 640, 0x4A }, { 704, 0x4B }, { 768, 0x4C }, { 832, 0x4D }, { 896, 0x72 }, { 960, 0x73 }, { 1024, 0x74 }, { 1088, 0x75 }, { 1152, 0x76 }, { 1216, 0x77 }, { 1280, 0x52 }, { 1344, 0x53 }, diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 3409b3dd8..9a953a269 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -343,7 +343,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff switch (bitsPerPixel) { case TiffBitsPerPixel.Bit1: - if (compression == TiffCompression.Ccitt1D || compression == TiffCompression.CcittGroup3Fax || compression == TiffCompression.CcittGroup4Fax) + if (IsOneBitCompression(compression)) { // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.WhiteIsZero, compression, TiffPredictor.None); @@ -375,6 +375,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; } + // Make sure 1 Bit compression is only used with 1 bit pixel type. + if (IsOneBitCompression(this.CompressionType) && this.BitsPerPixel != TiffBitsPerPixel.Bit1) + { + // Invalid compression / bits per pixel combination, fallback to no compression. + this.CompressionType = DefaultCompression; + } + return; } @@ -396,18 +403,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff { case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.WhiteIsZero: - if (this.CompressionType == TiffCompression.Ccitt1D || - this.CompressionType == TiffCompression.CcittGroup3Fax || - this.CompressionType == TiffCompression.CcittGroup4Fax) + if (IsOneBitCompression(this.CompressionType)) { this.SetEncoderOptions(TiffBitsPerPixel.Bit1, photometricInterpretation, compression, TiffPredictor.None); return; } - else - { - this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); - return; - } + + this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); + return; case TiffPhotometricInterpretation.PaletteColor: this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); @@ -428,5 +431,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.CompressionType = compression; this.HorizontalPredictor = predictor; } + + public static bool IsOneBitCompression(TiffCompression? compression) + { + if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax) + { + return true; + } + + return false; + } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 93ca611c9..d5f6df4a5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -95,6 +95,28 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(TiffBitsPerPixel.Bit24, frameMetaData.BitsPerPixel); } + [Theory] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Ccitt1D)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.CcittGroup3Fax)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.CcittGroup4Fax)] + public void EncoderOptions_WithInvalidCompressionAndPixelTypeCombination_DefaultsToRgb(TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression) + { + // arrange + var tiffEncoder = new TiffEncoder { PhotometricInterpretation = photometricInterpretation, Compression = compression }; + using Image input = new Image(10, 10); + using var memStream = new MemoryStream(); + + // act + input.Save(memStream, tiffEncoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(memStream); + + TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + Assert.Equal(TiffBitsPerPixel.Bit24, frameMetaData.BitsPerPixel); + } + [Theory] [InlineData(null, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] From 5675aa82ab14ac37e620a5fb0eef4ad5df81db2e Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 3 Jun 2022 15:38:41 +0200 Subject: [PATCH 29/30] Fix encode tiff benchmarks - Deflate and packbits are not supported by System.Drawing - When 1d Compression is chosen, TiffPhotometricInterpretation should be WhiteIsZero --- .../Codecs/Tiff/EncodeTiff.cs | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs index e9c61e729..da2d94a2a 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Drawing.Imaging; using System.IO; using BenchmarkDotNet.Attributes; @@ -9,6 +10,7 @@ using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; +using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Benchmarks.Codecs { @@ -17,11 +19,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [Config(typeof(Config.ShortMultiFramework))] public class EncodeTiff { - private System.Drawing.Image drawing; + private Stream stream; + private SDImage drawing; private Image core; - private Configuration configuration; - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); [Params(TestImages.Tiff.Calliphora_RgbUncompressed)] @@ -29,9 +30,11 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [Params( TiffCompression.None, - TiffCompression.Deflate, + + // System.Drawing does not support Deflate or PackBits + // TiffCompression.Deflate, + // TiffCompression.PackBits, TiffCompression.Lzw, - TiffCompression.PackBits, TiffCompression.CcittGroup3Fax, TiffCompression.Ccitt1D)] public TiffCompression Compression { get; set; } @@ -39,11 +42,12 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [GlobalSetup] public void ReadImages() { - if (this.core == null) + if (this.stream == null) { - this.configuration = new Configuration(); - this.core = Image.Load(this.configuration, this.TestImageFullPath); - this.drawing = System.Drawing.Image.FromFile(this.TestImageFullPath); + this.stream = File.OpenRead(this.TestImageFullPath); + this.core = Image.Load(this.stream); + this.stream.Position = 0; + this.drawing = SDImage.FromStream(this.stream); } } @@ -70,7 +74,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [Benchmark(Description = "ImageSharp Tiff")] public void TiffCore() { - TiffPhotometricInterpretation photometricInterpretation = TiffPhotometricInterpretation.Rgb; + TiffPhotometricInterpretation photometricInterpretation = + IsOneBitCompression(this.Compression) ? + TiffPhotometricInterpretation.WhiteIsZero : + TiffPhotometricInterpretation.Rgb; var encoder = new TiffEncoder() { Compression = this.Compression, PhotometricInterpretation = photometricInterpretation }; using var memoryStream = new MemoryStream(); @@ -109,8 +116,18 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs return EncoderValue.CompressionLZW; default: - throw new System.NotSupportedException(compression.ToString()); + throw new NotSupportedException(compression.ToString()); + } + } + + public static bool IsOneBitCompression(TiffCompression compression) + { + if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax) + { + return true; } + + return false; } } } From 3af2dd7720ca35060348ea0a50e4e430b9015358 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 3 Jun 2022 15:39:59 +0200 Subject: [PATCH 30/30] Multiply by inv max value instead of dividing in GeneratePalette --- .../PaletteTiffColor{TPixel}.cs | 8 +++++--- .../BlackIsZeroTiffColorTests.cs | 19 +++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs index ad5793084..d0ab2383d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs @@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation private readonly TPixel[] palette; + private const float InvMax = 1.0f / 65535F; + /// The number of bits per sample for each pixel. /// The RGB color lookup table to use for decoding the image. public PaletteTiffColor(TiffBitsPerSample bitsPerSample, ushort[] colorMap) @@ -56,9 +58,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int i = 0; i < palette.Length; i++) { - float r = colorMap[rOffset + i] / 65535F; - float g = colorMap[gOffset + i] / 65535F; - float b = colorMap[bOffset + i] / 65535F; + float r = colorMap[rOffset + i] * InvMax; + float g = colorMap[gOffset + i] * InvMax; + float b = colorMap[bOffset + i] * InvMax; palette[i].FromScaledVector4(new Vector4(r, g, b, 1.0f)); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index 38611c6f3..d4964cf77 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -13,14 +13,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation [Trait("Format", "Tiff")] public class BlackIsZeroTiffColorTests : PhotometricInterpretationTestBase { - private static readonly Rgba32 Gray000 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Gray128 = new Rgba32(128, 128, 128, 255); - private static readonly Rgba32 Gray255 = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Gray0 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Gray8 = new Rgba32(136, 136, 136, 255); - private static readonly Rgba32 GrayF = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Bit0 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Bit1 = new Rgba32(255, 255, 255, 255); + private static readonly Rgba32 Gray000 = new(0, 0, 0, 255); + private static readonly Rgba32 Gray128 = new(128, 128, 128, 255); + private static readonly Rgba32 Gray255 = new(255, 255, 255, 255); + private static readonly Rgba32 Gray0 = new(0, 0, 0, 255); + private static readonly Rgba32 Gray8 = new(136, 136, 136, 255); + private static readonly Rgba32 GrayF = new(255, 255, 255, 255); + private static readonly Rgba32 Bit0 = new(0, 0, 0, 255); + private static readonly Rgba32 Bit1 = new(255, 255, 255, 255); private static readonly byte[] BilevelBytes4X4 = { @@ -30,8 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation 0b10010000 }; - private static readonly Rgba32[][] BilevelResult4X4 = new[] - { + private static readonly Rgba32[][] BilevelResult4X4 = { new[] { Bit0, Bit1, Bit0, Bit1 }, new[] { Bit1, Bit1, Bit1, Bit1 }, new[] { Bit0, Bit1, Bit1, Bit1 },