Skip to content

Commit edfc2b2

Browse files
authored
Merge branch 'master' into metadata
2 parents 92f47fc + 7a5cd86 commit edfc2b2

File tree

5 files changed

+110
-126
lines changed

5 files changed

+110
-126
lines changed

src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -167,18 +167,6 @@ private void ParseBaselineDataInterleaved()
167167
int mcusPerLine = this.frame.McusPerLine;
168168
ref HuffmanScanBuffer buffer = ref this.scanBuffer;
169169

170-
// Pre-derive the huffman table to avoid in-loop checks.
171-
for (int i = 0; i < this.componentsCount; i++)
172-
{
173-
int order = this.frame.ComponentOrder[i];
174-
JpegComponent component = this.components[order];
175-
176-
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
177-
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
178-
dcHuffmanTable.Configure();
179-
acHuffmanTable.Configure();
180-
}
181-
182170
for (int j = 0; j < mcusPerColumn; j++)
183171
{
184172
this.cancellationToken.ThrowIfCancellationRequested();
@@ -248,8 +236,6 @@ private void ParseBaselineDataNonInterleaved()
248236

249237
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
250238
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
251-
dcHuffmanTable.Configure();
252-
acHuffmanTable.Configure();
253239

254240
for (int j = 0; j < h; j++)
255241
{
@@ -347,15 +333,6 @@ private void ParseProgressiveDataInterleaved()
347333
int mcusPerLine = this.frame.McusPerLine;
348334
ref HuffmanScanBuffer buffer = ref this.scanBuffer;
349335

350-
// Pre-derive the huffman table to avoid in-loop checks.
351-
for (int k = 0; k < this.componentsCount; k++)
352-
{
353-
int order = this.frame.ComponentOrder[k];
354-
JpegComponent component = this.components[order];
355-
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
356-
dcHuffmanTable.Configure();
357-
}
358-
359336
for (int j = 0; j < mcusPerColumn; j++)
360337
{
361338
for (int i = 0; i < mcusPerLine; i++)
@@ -416,7 +393,6 @@ private void ParseProgressiveDataNonInterleaved()
416393
if (this.SpectralStart == 0)
417394
{
418395
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
419-
dcHuffmanTable.Configure();
420396

421397
for (int j = 0; j < h; j++)
422398
{
@@ -444,7 +420,6 @@ ref Unsafe.Add(ref blockRef, i),
444420
else
445421
{
446422
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
447-
acHuffmanTable.Configure();
448423

449424
for (int j = 0; j < h; j++)
450425
{
@@ -752,11 +727,12 @@ private bool HandleRestart()
752727
/// <param name="index">Table index.</param>
753728
/// <param name="codeLengths">Code lengths.</param>
754729
/// <param name="values">Code values.</param>
730+
/// <param name="workspace">The provided spare workspace memory, can be dirty.</param>
755731
[MethodImpl(InliningOptions.ShortMethod)]
756-
public void BuildHuffmanTable(int type, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values)
732+
public void BuildHuffmanTable(int type, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values, Span<uint> workspace)
757733
{
758734
HuffmanTable[] tables = type == 0 ? this.dcHuffmanTables : this.acHuffmanTables;
759-
tables[index] = new HuffmanTable(codeLengths, values);
735+
tables[index] = new HuffmanTable(codeLengths, values, workspace);
760736
}
761737
}
762738
}

src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs

Lines changed: 28 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
1313
[StructLayout(LayoutKind.Sequential)]
1414
internal unsafe struct HuffmanTable
1515
{
16-
private bool isConfigured;
17-
1816
/// <summary>
19-
/// Derived from the DHT marker. Sizes[k] = # of symbols with codes of length k bits; Sizes[0] is unused.
17+
/// Memory workspace buffer size used in <see cref="HuffmanTable"/> ctor.
2018
/// </summary>
21-
public fixed byte Sizes[17];
19+
public const int WorkspaceByteSize = 256 * sizeof(uint);
2220

2321
/// <summary>
2422
/// Derived from the DHT marker. Contains the symbols, in order of incremental code length.
@@ -58,51 +56,35 @@ internal unsafe struct HuffmanTable
5856
/// <summary>
5957
/// Initializes a new instance of the <see cref="HuffmanTable"/> struct.
6058
/// </summary>
61-
/// <param name="codeLengths">The code lengths</param>
62-
/// <param name="values">The huffman values</param>
63-
public HuffmanTable(ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values)
59+
/// <param name="codeLengths">The code lengths.</param>
60+
/// <param name="values">The huffman values.</param>
61+
/// <param name="workspace">The provided spare workspace memory, can be dirty.</param>
62+
public HuffmanTable(ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values, Span<uint> workspace)
6463
{
65-
this.isConfigured = false;
66-
Unsafe.CopyBlockUnaligned(ref this.Sizes[0], ref MemoryMarshal.GetReference(codeLengths), (uint)codeLengths.Length);
6764
Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), (uint)values.Length);
68-
}
69-
70-
/// <summary>
71-
/// Expands the HuffmanTable into its readable form.
72-
/// </summary>
73-
public void Configure()
74-
{
75-
if (this.isConfigured)
76-
{
77-
return;
78-
}
7965

80-
Span<char> huffSize = stackalloc char[257];
81-
Span<uint> huffCode = stackalloc uint[257];
82-
83-
// Figure C.1: make table of Huffman code length for each symbol
66+
// Generate codes
67+
uint code = 0;
68+
int si = 1;
8469
int p = 0;
85-
for (int j = 1; j <= 16; j++)
70+
for (int i = 1; i <= 16; i++)
8671
{
87-
int i = this.Sizes[j];
88-
while (i-- != 0)
72+
int count = codeLengths[i];
73+
for (int j = 0; j < count; j++)
8974
{
90-
huffSize[p++] = (char)j;
75+
workspace[p++] = code;
76+
code++;
9177
}
92-
}
9378

94-
huffSize[p] = (char)0;
95-
96-
// Figure C.2: generate the codes themselves
97-
uint code = 0;
98-
int si = huffSize[0];
99-
p = 0;
100-
while (huffSize[p] != 0)
101-
{
102-
while (huffSize[p] == si)
79+
// 'code' is now 1 more than the last code used for codelength 'si'
80+
// in the valid worst possible case 'code' would have the least
81+
// significant bit set to 1, e.g. 1111(0) +1 => 1111(1)
82+
// but it must still fit in 'si' bits since no huffman code can be equal to all 1s
83+
// if last code is all ones, e.g. 1111(1), then incrementing it by 1 would yield
84+
// a new code which occupies one extra bit, e.g. 1111(1) +1 => (1)1111(0)
85+
if (code >= (1 << si))
10386
{
104-
huffCode[p++] = code;
105-
code++;
87+
JpegThrowHelper.ThrowInvalidImageContentException("Bad huffman table.");
10688
}
10789

10890
code <<= 1;
@@ -113,11 +95,11 @@ public void Configure()
11395
p = 0;
11496
for (int j = 1; j <= 16; j++)
11597
{
116-
if (this.Sizes[j] != 0)
98+
if (codeLengths[j] != 0)
11799
{
118-
this.ValOffset[j] = p - (int)huffCode[p];
119-
p += this.Sizes[j];
120-
this.MaxCode[j] = huffCode[p - 1]; // Maximum code of length l
100+
this.ValOffset[j] = p - (int)workspace[p];
101+
p += codeLengths[j];
102+
this.MaxCode[j] = workspace[p - 1]; // Maximum code of length l
121103
this.MaxCode[j] <<= JpegConstants.Huffman.RegisterSize - j; // Left justify
122104
this.MaxCode[j] |= (1ul << (JpegConstants.Huffman.RegisterSize - j)) - 1;
123105
}
@@ -142,11 +124,11 @@ public void Configure()
142124
for (int length = 1; length <= JpegConstants.Huffman.LookupBits; length++)
143125
{
144126
int jShift = JpegConstants.Huffman.LookupBits - length;
145-
for (int i = 1; i <= this.Sizes[length]; i++, p++)
127+
for (int i = 1; i <= codeLengths[length]; i++, p++)
146128
{
147129
// length = current code's length, p = its index in huffCode[] & Values[].
148130
// Generate left-justified code followed by all possible bit sequences
149-
int lookBits = (int)(huffCode[p] << jShift);
131+
int lookBits = (int)(workspace[p] << jShift);
150132
for (int ctr = 1 << (JpegConstants.Huffman.LookupBits - length); ctr > 0; ctr--)
151133
{
152134
this.LookaheadSize[lookBits] = (byte)length;
@@ -155,8 +137,6 @@ public void Configure()
155137
}
156138
}
157139
}
158-
159-
this.isConfigured = true;
160140
}
161141
}
162142
}

src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,11 @@ private void ProcessApp13Marker(BufferedReadStream stream, int remaining)
788788
}
789789
}
790790
}
791+
else
792+
{
793+
// If the profile is unknown skip over the rest of it.
794+
stream.Skip(remaining);
795+
}
791796
}
792797

793798
/// <summary>
@@ -1095,12 +1100,18 @@ private void ProcessStartOfFrameMarker(BufferedReadStream stream, int remaining,
10951100
/// <param name="remaining">The remaining bytes in the segment block.</param>
10961101
private void ProcessDefineHuffmanTablesMarker(BufferedReadStream stream, int remaining)
10971102
{
1098-
int length = remaining;
1103+
const int codeLengthsByteSize = 17;
1104+
const int codeValuesMaxByteSize = 256;
1105+
const int totalBufferSize = codeLengthsByteSize + codeValuesMaxByteSize + HuffmanTable.WorkspaceByteSize;
10991106

1100-
using (IMemoryOwner<byte> huffmanData = this.Configuration.MemoryAllocator.Allocate<byte>(256, AllocationOptions.Clean))
1107+
int length = remaining;
1108+
using (IMemoryOwner<byte> buffer = this.Configuration.MemoryAllocator.Allocate<byte>(totalBufferSize))
11011109
{
1102-
Span<byte> huffmanDataSpan = huffmanData.GetSpan();
1103-
ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanDataSpan);
1110+
Span<byte> bufferSpan = buffer.GetSpan();
1111+
Span<byte> huffmanLegthsSpan = bufferSpan.Slice(0, codeLengthsByteSize);
1112+
Span<byte> huffmanValuesSpan = bufferSpan.Slice(codeLengthsByteSize, codeValuesMaxByteSize);
1113+
Span<uint> tableWorkspace = MemoryMarshal.Cast<byte, uint>(bufferSpan.Slice(codeLengthsByteSize + codeValuesMaxByteSize));
1114+
11041115
for (int i = 2; i < remaining;)
11051116
{
11061117
byte huffmanTableSpec = (byte)stream.ReadByte();
@@ -1110,49 +1121,40 @@ private void ProcessDefineHuffmanTablesMarker(BufferedReadStream stream, int rem
11101121
// Types 0..1 DC..AC
11111122
if (tableType > 1)
11121123
{
1113-
JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table type: {tableType}");
1124+
JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table type: {tableType}.");
11141125
}
11151126

11161127
// Max tables of each type
11171128
if (tableIndex > 3)
11181129
{
1119-
JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table index: {tableIndex}");
1130+
JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table index: {tableIndex}.");
11201131
}
11211132

1122-
stream.Read(huffmanDataSpan, 0, 16);
1133+
stream.Read(huffmanLegthsSpan, 1, 16);
11231134

1124-
using (IMemoryOwner<byte> codeLengths = this.Configuration.MemoryAllocator.Allocate<byte>(17, AllocationOptions.Clean))
1135+
int codeLengthSum = 0;
1136+
for (int j = 1; j < 17; j++)
11251137
{
1126-
Span<byte> codeLengthsSpan = codeLengths.GetSpan();
1127-
ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengthsSpan);
1128-
int codeLengthSum = 0;
1129-
1130-
for (int j = 1; j < 17; j++)
1131-
{
1132-
codeLengthSum += Unsafe.Add(ref codeLengthsRef, j) = Unsafe.Add(ref huffmanDataRef, j - 1);
1133-
}
1138+
codeLengthSum += huffmanLegthsSpan[j];
1139+
}
11341140

1135-
length -= 17;
1141+
length -= 17;
11361142

1137-
if (codeLengthSum > 256 || codeLengthSum > length)
1138-
{
1139-
JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length.");
1140-
}
1143+
if (codeLengthSum > 256 || codeLengthSum > length)
1144+
{
1145+
JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length.");
1146+
}
11411147

1142-
using (IMemoryOwner<byte> huffmanValues = this.Configuration.MemoryAllocator.Allocate<byte>(256, AllocationOptions.Clean))
1143-
{
1144-
Span<byte> huffmanValuesSpan = huffmanValues.GetSpan();
1145-
stream.Read(huffmanValuesSpan, 0, codeLengthSum);
1148+
stream.Read(huffmanValuesSpan, 0, codeLengthSum);
11461149

1147-
i += 17 + codeLengthSum;
1150+
i += 17 + codeLengthSum;
11481151

1149-
this.scanDecoder.BuildHuffmanTable(
1150-
tableType,
1151-
tableIndex,
1152-
codeLengthsSpan,
1153-
huffmanValuesSpan);
1154-
}
1155-
}
1152+
this.scanDecoder.BuildHuffmanTable(
1153+
tableType,
1154+
tableIndex,
1155+
huffmanLegthsSpan,
1156+
huffmanValuesSpan.Slice(0, codeLengthSum),
1157+
tableWorkspace);
11561158
}
11571159
}
11581160
}

0 commit comments

Comments
 (0)