diff --git a/src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanTable.cs index 85bcb5dfe0..4c475450bd 100644 --- a/src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanTable.cs @@ -16,13 +16,9 @@ namespace ImageSharp.Formats.Jpeg.Port.Components internal struct HuffmanTable : IDisposable { private Buffer lookahead; - private Buffer huffcode; - private Buffer huffsize; private Buffer valOffset; private Buffer maxcode; - private Buffer huffval; - private Buffer bits; /// /// Initializes a new instance of the struct. @@ -32,119 +28,113 @@ namespace ImageSharp.Formats.Jpeg.Port.Components public HuffmanTable(byte[] lengths, byte[] values) { this.lookahead = Buffer.CreateClean(256); - this.huffcode = Buffer.CreateClean(257); - this.huffsize = Buffer.CreateClean(257); this.valOffset = Buffer.CreateClean(18); this.maxcode = Buffer.CreateClean(18); + using (var huffsize = Buffer.CreateClean(257)) + using (var huffcode = Buffer.CreateClean(257)) + { + GenerateSizeTable(lengths, huffsize); + GenerateCodeTable(huffsize, huffcode); + GenerateDecoderTables(lengths, huffcode, this.valOffset, this.maxcode); + GenerateLookaheadTables(lengths, values, this.lookahead); + } + this.huffval = Buffer.CreateClean(values.Length); Buffer.BlockCopy(values, 0, this.huffval.Array, 0, values.Length); - this.bits = Buffer.CreateClean(lengths.Length); - Buffer.BlockCopy(lengths, 0, this.bits.Array, 0, lengths.Length); - - this.GenerateSizeTable(); - this.GenerateCodeTable(); - this.GenerateDecoderTables(); - this.GenerateLookaheadTables(); + this.MaxCode = this.maxcode.Array; + this.ValOffset = this.valOffset.Array; + this.HuffVal = this.huffval.Array; + this.Lookahead = this.lookahead.Array; } /// - /// Gets the Huffman value code at the given index + /// Gets the max code array /// - /// The index - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public short GetHuffVal(int i) + public long[] MaxCode { - return this.huffval[i]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; } /// - /// Gets the max code at the given index + /// Gets the value offset array /// - /// The index - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long GetMaxCode(int i) + public short[] ValOffset { - return this.maxcode[i]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; } /// - /// Gets the index to the locatation of the huffman value + /// Gets the huffman value array /// - /// The index - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetValOffset(int i) + public byte[] HuffVal { - return this.valOffset[i]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; } /// - /// Gets the look ahead table balue + /// Gets the lookahead array /// - /// The index - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetLookAhead(int i) + public short[] Lookahead { - return this.lookahead[i]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; } /// public void Dispose() { this.lookahead?.Dispose(); - this.huffcode?.Dispose(); - this.huffsize?.Dispose(); this.valOffset?.Dispose(); this.maxcode?.Dispose(); this.huffval?.Dispose(); - this.bits?.Dispose(); this.lookahead = null; - this.huffcode = null; - this.huffsize = null; this.valOffset = null; this.maxcode = null; this.huffval = null; - this.bits = null; } /// /// Figure C.1: make table of Huffman code length for each symbol /// - private void GenerateSizeTable() + /// The code lengths + /// The huffman size span + private static void GenerateSizeTable(byte[] lengths, Span huffsize) { short index = 0; for (short l = 1; l <= 16; l++) { - byte i = this.bits[l]; + byte i = lengths[l]; for (short j = 0; j < i; j++) { - this.huffsize[index] = l; + huffsize[index] = l; index++; } } - this.huffsize[index] = 0; + huffsize[index] = 0; } /// /// Figure C.2: generate the codes themselves /// - private void GenerateCodeTable() + /// The huffman size span + /// The huffman code span + private static void GenerateCodeTable(Span huffsize, Span huffcode) { short k = 0; - short si = this.huffsize[0]; + short si = huffsize[0]; short code = 0; - for (short i = 0; i < this.huffsize.Length; i++) + for (short i = 0; i < huffsize.Length; i++) { - while (this.huffsize[k] == si) + while (huffsize[k] == si) { - this.huffcode[k] = code; + huffcode[k] = code; code++; k++; } @@ -157,33 +147,40 @@ namespace ImageSharp.Formats.Jpeg.Port.Components /// /// Figure F.15: generate decoding tables for bit-sequential decoding /// - private void GenerateDecoderTables() + /// The code lengths + /// The huffman code span + /// The value offset span + /// The max code span + private static void GenerateDecoderTables(byte[] lengths, Span huffcode, Span valOffset, Span maxcode) { short bitcount = 0; for (int i = 1; i <= 16; i++) { - if (this.bits[i] != 0) + if (lengths[i] != 0) { // valoffset[l] = huffval[] index of 1st symbol of code length i, // minus the minimum code of length i - this.valOffset[i] = (short)(bitcount - this.huffcode[bitcount]); - bitcount += this.bits[i]; - this.maxcode[i] = this.huffcode[bitcount - 1]; // maximum code of length i + valOffset[i] = (short)(bitcount - huffcode[bitcount]); + bitcount += lengths[i]; + maxcode[i] = huffcode[bitcount - 1]; // maximum code of length i } else { - this.maxcode[i] = -1; // -1 if no codes of this length + maxcode[i] = -1; // -1 if no codes of this length } } - this.valOffset[17] = 0; - this.maxcode[17] = 0xFFFFFL; + valOffset[17] = 0; + maxcode[17] = 0xFFFFFL; } /// /// Generates lookup tables to speed up decoding /// - private void GenerateLookaheadTables() + /// The code lengths + /// The huffman value array + /// The lookahead span + private static void GenerateLookaheadTables(byte[] lengths, byte[] huffval, Span lookahead) { int x = 0, code = 0; @@ -191,7 +188,7 @@ namespace ImageSharp.Formats.Jpeg.Port.Components { code <<= 1; - for (int j = 0; j < this.bits[i + 1]; j++) + for (int j = 0; j < lengths[i + 1]; j++) { // The codeLength is 1+i, so shift code by 8-(1+i) to // calculate the high bits for every 8-bit sequence @@ -199,11 +196,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components // The high 8 bits of lutValue are the encoded value. // The low 8 bits are 1 plus the codeLength. byte base2 = (byte)(code << (7 - i)); - short lutValue = (short)((short)(this.huffval[x] << 8) | (short)(2 + i)); + short lutValue = (short)((short)(huffval[x] << 8) | (short)(2 + i)); for (int k = 0; k < 1 << (7 - i); k++) { - this.lookahead[base2 | k] = lutValue; + lookahead[base2 | k] = lutValue; } code++; diff --git a/src/ImageSharp/Formats/Jpeg/Port/Components/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Port/Components/ScanDecoder.cs index 98d46dae5f..23288f0366 100644 --- a/src/ImageSharp/Formats/Jpeg/Port/Components/ScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Port/Components/ScanDecoder.cs @@ -635,7 +635,7 @@ namespace ImageSharp.Formats.Jpeg.Port.Components // } // this.accumulator = (this.accumulator << 8) | this.bitsData; // int lutIndex = (this.accumulator >> (this.bitsUnRead - 8)) & 0xFF; - // int v = tree.GetLookAhead(lutIndex); + // int v = tree.Lookahead[lutIndex]; // if (v != 0) // { // int nb = (v & 0xFF) - 1; @@ -657,7 +657,7 @@ namespace ImageSharp.Formats.Jpeg.Port.Components // "DECODE", section F.2.2.3, figure F.16, page 109 of T.81 int i = 1; - while (code > tree.GetMaxCode(i)) + while (code > tree.MaxCode[i]) { code <<= 1; code |= (short)this.ReadBit(stream); @@ -670,8 +670,8 @@ namespace ImageSharp.Formats.Jpeg.Port.Components i++; } - int j = tree.GetValOffset(i); - return tree.GetHuffVal((j + code) & 0xFF); + int j = tree.ValOffset[i]; + return tree.HuffVal[(j + code) & 0xFF]; } [MethodImpl(MethodImplOptions.AggressiveInlining)]