Skip to content

Commit a194b9e

Browse files
authored
Merge pull request #2134 from SixLabors/bp/Issue2132
Tiff: Performance improvements for Fax4 decompression
2 parents e7a10b0 + d02854f commit a194b9e

File tree

51 files changed

+545
-351
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+545
-351
lines changed

src/ImageSharp/Common/Helpers/Numerics.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ public static int LeastCommonMultiple(int a, int b)
7575
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7676
public static int Modulo8(int x) => x & 7;
7777

78+
/// <summary>
79+
/// Calculates <paramref name="x"/> % 8
80+
/// </summary>
81+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
82+
public static nint Modulo8(nint x) => x & 7;
83+
7884
/// <summary>
7985
/// Fast (x mod m) calculator, with the restriction that
8086
/// <paramref name="m"/> should be power of 2.

src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,23 @@
22
// Licensed under the Apache License, Version 2.0.
33

44
using System;
5+
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
57

68
namespace SixLabors.ImageSharp.Formats.Tiff.Compression
79
{
810
internal static class BitWriterUtils
911
{
10-
public static void WriteBits(Span<byte> buffer, int pos, uint count, byte value)
12+
public static void WriteBits(Span<byte> buffer, nint pos, nint count, byte value)
1113
{
12-
int bitPos = pos % 8;
13-
int bufferPos = pos / 8;
14-
int startIdx = bufferPos + bitPos;
15-
int endIdx = (int)(startIdx + count);
14+
nint bitPos = Numerics.Modulo8(pos);
15+
nint bufferPos = pos / 8;
16+
nint startIdx = bufferPos + bitPos;
17+
nint endIdx = startIdx + count;
1618

1719
if (value == 1)
1820
{
19-
for (int i = startIdx; i < endIdx; i++)
21+
for (nint i = startIdx; i < endIdx; i++)
2022
{
2123
WriteBit(buffer, bufferPos, bitPos);
2224

@@ -30,7 +32,7 @@ public static void WriteBits(Span<byte> buffer, int pos, uint count, byte value)
3032
}
3133
else
3234
{
33-
for (int i = startIdx; i < endIdx; i++)
35+
for (nint i = startIdx; i < endIdx; i++)
3436
{
3537
WriteZeroBit(buffer, bufferPos, bitPos);
3638

@@ -44,8 +46,18 @@ public static void WriteBits(Span<byte> buffer, int pos, uint count, byte value)
4446
}
4547
}
4648

47-
public static void WriteBit(Span<byte> buffer, int bufferPos, int bitPos) => buffer[bufferPos] |= (byte)(1 << (7 - bitPos));
49+
[MethodImpl(InliningOptions.ShortMethod)]
50+
public static void WriteBit(Span<byte> buffer, nint bufferPos, nint bitPos)
51+
{
52+
ref byte b = ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), bufferPos);
53+
b |= (byte)(1 << (int)(7 - bitPos));
54+
}
4855

49-
public static void WriteZeroBit(Span<byte> buffer, int bufferPos, int bitPos) => buffer[bufferPos] = (byte)(buffer[bufferPos] & ~(1 << (7 - bitPos)));
56+
[MethodImpl(InliningOptions.ShortMethod)]
57+
public static void WriteZeroBit(Span<byte> buffer, nint bufferPos, nint bitPos)
58+
{
59+
ref byte b = ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), bufferPos);
60+
b = (byte)(b & ~(1 << (int)(7 - bitPos)));
61+
}
5062
}
5163
}

src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,28 @@ internal abstract class TiffCcittCompressor : TiffBaseCompressor
2323
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
2424
};
2525

26-
private static readonly Dictionary<uint, uint> WhiteLen4TermCodes = new Dictionary<uint, uint>()
26+
private static readonly Dictionary<uint, uint> WhiteLen4TermCodes = new()
2727
{
2828
{ 2, 0x7 }, { 3, 0x8 }, { 4, 0xB }, { 5, 0xC }, { 6, 0xE }, { 7, 0xF }
2929
};
3030

31-
private static readonly Dictionary<uint, uint> WhiteLen5TermCodes = new Dictionary<uint, uint>()
31+
private static readonly Dictionary<uint, uint> WhiteLen5TermCodes = new()
3232
{
3333
{ 8, 0x13 }, { 9, 0x14 }, { 10, 0x7 }, { 11, 0x8 }
3434
};
3535

36-
private static readonly Dictionary<uint, uint> WhiteLen6TermCodes = new Dictionary<uint, uint>()
36+
private static readonly Dictionary<uint, uint> WhiteLen6TermCodes = new()
3737
{
3838
{ 1, 0x7 }, { 12, 0x8 }, { 13, 0x3 }, { 14, 0x34 }, { 15, 0x35 }, { 16, 0x2A }, { 17, 0x2B }
3939
};
4040

41-
private static readonly Dictionary<uint, uint> WhiteLen7TermCodes = new Dictionary<uint, uint>()
41+
private static readonly Dictionary<uint, uint> WhiteLen7TermCodes = new()
4242
{
4343
{ 18, 0x27 }, { 19, 0xC }, { 20, 0x8 }, { 21, 0x17 }, { 22, 0x3 }, { 23, 0x4 }, { 24, 0x28 }, { 25, 0x2B }, { 26, 0x13 },
4444
{ 27, 0x24 }, { 28, 0x18 }
4545
};
4646

47-
private static readonly Dictionary<uint, uint> WhiteLen8TermCodes = new Dictionary<uint, uint>()
47+
private static readonly Dictionary<uint, uint> WhiteLen8TermCodes = new()
4848
{
4949
{ 0, WhiteZeroRunTermCode }, { 29, 0x2 }, { 30, 0x3 }, { 31, 0x1A }, { 32, 0x1B }, { 33, 0x12 }, { 34, 0x13 }, { 35, 0x14 },
5050
{ 36, 0x15 }, { 37, 0x16 }, { 38, 0x17 }, { 39, 0x28 }, { 40, 0x29 }, { 41, 0x2A }, { 42, 0x2B }, { 43, 0x2C }, { 44, 0x2D },
@@ -53,57 +53,57 @@ internal abstract class TiffCcittCompressor : TiffBaseCompressor
5353
{ 63, 0x34 }
5454
};
5555

56-
private static readonly Dictionary<uint, uint> BlackLen2TermCodes = new Dictionary<uint, uint>()
56+
private static readonly Dictionary<uint, uint> BlackLen2TermCodes = new()
5757
{
5858
{ 2, 0x3 }, { 3, 0x2 }
5959
};
6060

61-
private static readonly Dictionary<uint, uint> BlackLen3TermCodes = new Dictionary<uint, uint>()
61+
private static readonly Dictionary<uint, uint> BlackLen3TermCodes = new()
6262
{
6363
{ 1, 0x2 }, { 4, 0x3 }
6464
};
6565

66-
private static readonly Dictionary<uint, uint> BlackLen4TermCodes = new Dictionary<uint, uint>()
66+
private static readonly Dictionary<uint, uint> BlackLen4TermCodes = new()
6767
{
6868
{ 5, 0x3 }, { 6, 0x2 }
6969
};
7070

71-
private static readonly Dictionary<uint, uint> BlackLen5TermCodes = new Dictionary<uint, uint>()
71+
private static readonly Dictionary<uint, uint> BlackLen5TermCodes = new()
7272
{
7373
{ 7, 0x3 }
7474
};
7575

76-
private static readonly Dictionary<uint, uint> BlackLen6TermCodes = new Dictionary<uint, uint>()
76+
private static readonly Dictionary<uint, uint> BlackLen6TermCodes = new()
7777
{
7878
{ 8, 0x5 }, { 9, 0x4 }
7979
};
8080

81-
private static readonly Dictionary<uint, uint> BlackLen7TermCodes = new Dictionary<uint, uint>()
81+
private static readonly Dictionary<uint, uint> BlackLen7TermCodes = new()
8282
{
8383
{ 10, 0x4 }, { 11, 0x5 }, { 12, 0x7 }
8484
};
8585

86-
private static readonly Dictionary<uint, uint> BlackLen8TermCodes = new Dictionary<uint, uint>()
86+
private static readonly Dictionary<uint, uint> BlackLen8TermCodes = new()
8787
{
8888
{ 13, 0x4 }, { 14, 0x7 }
8989
};
9090

91-
private static readonly Dictionary<uint, uint> BlackLen9TermCodes = new Dictionary<uint, uint>()
91+
private static readonly Dictionary<uint, uint> BlackLen9TermCodes = new()
9292
{
9393
{ 15, 0x18 }
9494
};
9595

96-
private static readonly Dictionary<uint, uint> BlackLen10TermCodes = new Dictionary<uint, uint>()
96+
private static readonly Dictionary<uint, uint> BlackLen10TermCodes = new()
9797
{
9898
{ 0, BlackZeroRunTermCode }, { 16, 0x17 }, { 17, 0x18 }, { 18, 0x8 }
9999
};
100100

101-
private static readonly Dictionary<uint, uint> BlackLen11TermCodes = new Dictionary<uint, uint>()
101+
private static readonly Dictionary<uint, uint> BlackLen11TermCodes = new()
102102
{
103103
{ 19, 0x67 }, { 20, 0x68 }, { 21, 0x6C }, { 22, 0x37 }, { 23, 0x28 }, { 24, 0x17 }, { 25, 0x18 }
104104
};
105105

106-
private static readonly Dictionary<uint, uint> BlackLen12TermCodes = new Dictionary<uint, uint>()
106+
private static readonly Dictionary<uint, uint> BlackLen12TermCodes = new()
107107
{
108108
{ 26, 0xCA }, { 27, 0xCB }, { 28, 0xCC }, { 29, 0xCD }, { 30, 0x68 }, { 31, 0x69 }, { 32, 0x6A }, { 33, 0x6B }, { 34, 0xD2 },
109109
{ 35, 0xD3 }, { 36, 0xD4 }, { 37, 0xD5 }, { 38, 0xD6 }, { 39, 0xD7 }, { 40, 0x6C }, { 41, 0x6D }, { 42, 0xDA }, { 43, 0xDB },
@@ -112,62 +112,62 @@ internal abstract class TiffCcittCompressor : TiffBaseCompressor
112112
{ 62, 0x66 }, { 63, 0x67 }
113113
};
114114

115-
private static readonly Dictionary<uint, uint> WhiteLen5MakeupCodes = new Dictionary<uint, uint>()
115+
private static readonly Dictionary<uint, uint> WhiteLen5MakeupCodes = new()
116116
{
117117
{ 64, 0x1B }, { 128, 0x12 }
118118
};
119119

120-
private static readonly Dictionary<uint, uint> WhiteLen6MakeupCodes = new Dictionary<uint, uint>()
120+
private static readonly Dictionary<uint, uint> WhiteLen6MakeupCodes = new()
121121
{
122122
{ 192, 0x17 }, { 1664, 0x18 }
123123
};
124124

125-
private static readonly Dictionary<uint, uint> WhiteLen8MakeupCodes = new Dictionary<uint, uint>()
125+
private static readonly Dictionary<uint, uint> WhiteLen8MakeupCodes = new()
126126
{
127127
{ 320, 0x36 }, { 384, 0x37 }, { 448, 0x64 }, { 512, 0x65 }, { 576, 0x68 }, { 640, 0x67 }
128128
};
129129

130-
private static readonly Dictionary<uint, uint> WhiteLen7MakeupCodes = new Dictionary<uint, uint>()
130+
private static readonly Dictionary<uint, uint> WhiteLen7MakeupCodes = new()
131131
{
132132
{ 256, 0x37 }
133133
};
134134

135-
private static readonly Dictionary<uint, uint> WhiteLen9MakeupCodes = new Dictionary<uint, uint>()
135+
private static readonly Dictionary<uint, uint> WhiteLen9MakeupCodes = new()
136136
{
137137
{ 704, 0xCC }, { 768, 0xCD }, { 832, 0xD2 }, { 896, 0xD3 }, { 960, 0xD4 }, { 1024, 0xD5 }, { 1088, 0xD6 },
138138
{ 1152, 0xD7 }, { 1216, 0xD8 }, { 1280, 0xD9 }, { 1344, 0xDA }, { 1408, 0xDB }, { 1472, 0x98 }, { 1536, 0x99 },
139139
{ 1600, 0x9A }, { 1728, 0x9B }
140140
};
141141

142-
private static readonly Dictionary<uint, uint> WhiteLen11MakeupCodes = new Dictionary<uint, uint>()
142+
private static readonly Dictionary<uint, uint> WhiteLen11MakeupCodes = new()
143143
{
144144
{ 1792, 0x8 }, { 1856, 0xC }, { 1920, 0xD }
145145
};
146146

147-
private static readonly Dictionary<uint, uint> WhiteLen12MakeupCodes = new Dictionary<uint, uint>()
147+
private static readonly Dictionary<uint, uint> WhiteLen12MakeupCodes = new()
148148
{
149149
{ 1984, 0x12 }, { 2048, 0x13 }, { 2112, 0x14 }, { 2176, 0x15 }, { 2240, 0x16 }, { 2304, 0x17 }, { 2368, 0x1C },
150150
{ 2432, 0x1D }, { 2496, 0x1E }, { 2560, 0x1F }
151151
};
152152

153-
private static readonly Dictionary<uint, uint> BlackLen10MakeupCodes = new Dictionary<uint, uint>()
153+
private static readonly Dictionary<uint, uint> BlackLen10MakeupCodes = new()
154154
{
155155
{ 64, 0xF }
156156
};
157157

158-
private static readonly Dictionary<uint, uint> BlackLen11MakeupCodes = new Dictionary<uint, uint>()
158+
private static readonly Dictionary<uint, uint> BlackLen11MakeupCodes = new()
159159
{
160160
{ 1792, 0x8 }, { 1856, 0xC }, { 1920, 0xD }
161161
};
162162

163-
private static readonly Dictionary<uint, uint> BlackLen12MakeupCodes = new Dictionary<uint, uint>()
163+
private static readonly Dictionary<uint, uint> BlackLen12MakeupCodes = new()
164164
{
165165
{ 128, 0xC8 }, { 192, 0xC9 }, { 256, 0x5B }, { 320, 0x33 }, { 384, 0x34 }, { 448, 0x35 },
166166
{ 1984, 0x12 }, { 2048, 0x13 }, { 2112, 0x14 }, { 2176, 0x15 }, { 2240, 0x16 }, { 2304, 0x17 }, { 2368, 0x1C },
167167
{ 2432, 0x1D }, { 2496, 0x1E }, { 2560, 0x1F }
168168
};
169169

170-
private static readonly Dictionary<uint, uint> BlackLen13MakeupCodes = new Dictionary<uint, uint>()
170+
private static readonly Dictionary<uint, uint> BlackLen13MakeupCodes = new()
171171
{
172172
{ 512, 0x6C }, { 576, 0x6D }, { 640, 0x4A }, { 704, 0x4B }, { 768, 0x4C }, { 832, 0x4D }, { 896, 0x72 },
173173
{ 960, 0x73 }, { 1024, 0x74 }, { 1088, 0x75 }, { 1152, 0x76 }, { 1216, 0x77 }, { 1280, 0x52 }, { 1344, 0x53 },
@@ -442,16 +442,16 @@ protected uint GetMakeupCode(uint runLength, out uint codeLength, bool isWhiteRu
442442
}
443443

444444
/// <summary>
445-
/// Pads output to the next byte
445+
/// Pads output to the next byte.
446446
/// </summary>
447447
/// <remarks>
448448
/// If the output is not currently on a byte boundary,
449-
/// zero-pad it to the next byte
449+
/// zero-pad it to the next byte.
450450
/// </remarks>
451451
protected void PadByte()
452452
{
453453
// Check if padding is necessary.
454-
if (this.bitPosition % 8 != 0)
454+
if (Numerics.Modulo8(this.bitPosition) != 0)
455455
{
456456
// Skip padding bits, move to next byte.
457457
this.bytePosition++;

src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittReferenceScanline.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0.
33

44
using System;
5+
using System.Runtime.CompilerServices;
56

67
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
78
{
@@ -130,6 +131,7 @@ private int FindB1ForNormalLine(int a0, byte a0Byte)
130131
return index + offset;
131132
}
132133

134+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
133135
private int FindB2ForImaginaryWhiteLine() => this.width;
134136

135137
private int FindB2ForNormalLine(int b1)

src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittTwoDimensionalCode.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,21 @@ internal readonly struct CcittTwoDimensionalCode
1313
/// <summary>
1414
/// Initializes a new instance of the <see cref="CcittTwoDimensionalCode"/> struct.
1515
/// </summary>
16-
/// <param name="type">The type.</param>
16+
/// <param name="code">The code word.</param>
17+
/// <param name="type">The type of the code.</param>
1718
/// <param name="bitsRequired">The bits required.</param>
1819
/// <param name="extensionBits">The extension bits.</param>
19-
public CcittTwoDimensionalCode(CcittTwoDimensionalCodeType type, int bitsRequired, int extensionBits = 0)
20-
=> this.value = (ushort)((byte)type | ((bitsRequired & 0b1111) << 8) | ((extensionBits & 0b111) << 11));
20+
public CcittTwoDimensionalCode(int code, CcittTwoDimensionalCodeType type, int bitsRequired, int extensionBits = 0)
21+
{
22+
this.Code = code;
23+
this.value = (ushort)((byte)type | ((bitsRequired & 0b1111) << 8) | ((extensionBits & 0b111) << 11));
24+
}
2125

2226
/// <summary>
2327
/// Gets the code type.
2428
/// </summary>
2529
public CcittTwoDimensionalCodeType Type => (CcittTwoDimensionalCodeType)(this.value & 0b11111111);
30+
31+
public int Code { get; }
2632
}
2733
}

src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Apache License, Version 2.0.
33

4-
using System.IO;
54
using SixLabors.ImageSharp.Formats.Tiff.Constants;
6-
using SixLabors.ImageSharp.Memory;
5+
using SixLabors.ImageSharp.IO;
76

87
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
98
{
@@ -19,14 +18,13 @@ internal sealed class ModifiedHuffmanBitReader : T4BitReader
1918
/// <param name="input">The compressed input stream.</param>
2019
/// <param name="fillOrder">The logical order of bits within a byte.</param>
2120
/// <param name="bytesToRead">The number of bytes to read from the stream.</param>
22-
/// <param name="allocator">The memory allocator.</param>
23-
public ModifiedHuffmanBitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator)
24-
: base(input, fillOrder, bytesToRead, allocator)
21+
public ModifiedHuffmanBitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead)
22+
: base(input, fillOrder, bytesToRead)
2523
{
2624
}
2725

2826
/// <inheritdoc/>
29-
public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || ((uint)(this.BitsRead - 1) < (7 - 1));
27+
public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || (uint)(this.BitsRead - 1) < 6;
3028

3129
/// <inheritdoc/>
3230
public override bool IsEndOfScanLine
@@ -53,12 +51,11 @@ public override void StartNewRow()
5351
{
5452
base.StartNewRow();
5553

56-
int remainder = this.BitsRead & 7; // bit-hack for % 8
54+
int remainder = Numerics.Modulo8(this.BitsRead);
5755
if (remainder != 0)
5856
{
5957
// Skip padding bits, move to next byte.
60-
this.Position++;
61-
this.ResetBitsRead();
58+
this.AdvancePosition();
6259
}
6360
}
6461

0 commit comments

Comments
 (0)