From 3f47e37229241fd236fbb050bd8d1dd5fb321f8d Mon Sep 17 00:00:00 2001 From: antonfirsov Date: Mon, 26 Dec 2016 19:09:53 +0100 Subject: [PATCH] moved Huffman table related code to HuffmanTree --- .../Jpg/Components/Decoder/HuffmanTree.cs | 154 ++++++++++++++++-- .../Jpg/Components/Decoder/JpegPixelArea.cs | 4 +- src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs | 150 ++--------------- 3 files changed, 156 insertions(+), 152 deletions(-) diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs index a148cc558..2c08284a3 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs @@ -12,6 +12,33 @@ namespace ImageSharp.Formats.Jpg /// internal struct HuffmanTree : IDisposable { + public static HuffmanTree[] CreateHuffmanTrees() + { + HuffmanTree[] result = new HuffmanTree[(MaxTc + 1) * (MaxTh + 1)]; + for (int i = 0; i < MaxTc + 1; i++) + { + for (int j = 0; j < MaxTh + 1; j++) + { + result[(i * ThRowSize) + j].Init(); + } + } + return result; + } + /// + /// The maximum (inclusive) number of codes in a Huffman tree. + /// + internal const int MaxNCodes = 256; + + /// + /// The maximum (inclusive) number of bits in a Huffman code. + /// + internal const int MaxCodeLength = 16; + + /// + /// The log-2 size of the Huffman decoder's look-up table. + /// + internal const int LutSize = 8; + /// /// Gets or sets the number of codes in the tree. /// @@ -47,25 +74,22 @@ namespace ImageSharp.Formats.Jpg /// public int[] Indices; - private static readonly ArrayPool UshortBuffer = ArrayPool.Create(1 << JpegDecoderCore.LutSize, 50); + private static readonly ArrayPool UshortBuffer = ArrayPool.Create(1 << LutSize, 50); - private static readonly ArrayPool ByteBuffer = ArrayPool.Create(JpegDecoderCore.MaxNCodes, 50); + private static readonly ArrayPool ByteBuffer = ArrayPool.Create(MaxNCodes, 50); - private static readonly ArrayPool IntBuffer = ArrayPool.Create(JpegDecoderCore.MaxCodeLength, 50); + private static readonly ArrayPool IntBuffer = ArrayPool.Create(MaxCodeLength, 50); /// /// Initializes the Huffman tree /// - /// Lut size - /// Max N codes - /// Max code length - public void Init(int lutSize, int maxNCodes, int maxCodeLength) + public void Init() { - this.Lut = UshortBuffer.Rent(1 << lutSize); - this.Values = ByteBuffer.Rent(maxNCodes); - this.MinCodes = IntBuffer.Rent(maxCodeLength); - this.MaxCodes = IntBuffer.Rent(maxCodeLength); - this.Indices = IntBuffer.Rent(maxCodeLength); + this.Lut = UshortBuffer.Rent(1 << LutSize); + this.Values = ByteBuffer.Rent(MaxNCodes); + this.MinCodes = IntBuffer.Rent(MaxCodeLength); + this.MaxCodes = IntBuffer.Rent(MaxCodeLength); + this.Indices = IntBuffer.Rent(MaxCodeLength); } /// @@ -79,5 +103,111 @@ namespace ImageSharp.Formats.Jpg IntBuffer.Return(this.MaxCodes, true); IntBuffer.Return(this.Indices, true); } + + /// + /// Internal part of the DHT processor, whatever does it mean + /// + /// The decoder instance + /// The temporal buffer that holds the data that has been read from stream + /// Remaining bits + internal void ProcessDefineHuffmanTablesMarkerLoop(JpegDecoderCore decoder, byte[] defineHuffmanTablesData, ref int remaining) + { + // Read nCodes and huffman.Valuess (and derive h.Length). + // nCodes[i] is the number of codes with code length i. + // h.Length is the total number of codes. + this.Length = 0; + + int[] ncodes = new int[MaxCodeLength]; + for (int i = 0; i < ncodes.Length; i++) + { + ncodes[i] = defineHuffmanTablesData[i + 1]; + this.Length += ncodes[i]; + } + + if (this.Length == 0) + { + throw new ImageFormatException("Huffman table has zero length"); + } + + if (this.Length > MaxNCodes) + { + throw new ImageFormatException("Huffman table has excessive length"); + } + + remaining -= this.Length + 17; + if (remaining < 0) + { + throw new ImageFormatException("DHT has wrong length"); + } + + decoder.ReadFull(this.Values, 0, this.Length); + + // Derive the look-up table. + for (int i = 0; i < this.Lut.Length; i++) + { + this.Lut[i] = 0; + } + + uint x = 0, code = 0; + + for (int i = 0; i < LutSize; i++) + { + code <<= 1; + + for (int j = 0; j < ncodes[i]; j++) + { + // The codeLength is 1+i, so shift code by 8-(1+i) to + // calculate the high bits for every 8-bit sequence + // whose codeLength's high bits matches code. + // 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)); + ushort lutValue = (ushort)((this.Values[x] << 8) | (2 + i)); + + for (int k = 0; k < 1 << (7 - i); k++) + { + this.Lut[base2 | k] = lutValue; + } + + code++; + x++; + } + } + + // Derive minCodes, maxCodes, and indices. + int c = 0, index = 0; + for (int i = 0; i < ncodes.Length; i++) + { + int nc = ncodes[i]; + if (nc == 0) + { + this.MinCodes[i] = -1; + this.MaxCodes[i] = -1; + this.Indices[i] = -1; + } + else + { + this.MinCodes[i] = c; + this.MaxCodes[i] = c + nc - 1; + this.Indices[i] = index; + c += nc; + index += nc; + } + + c <<= 1; + } + } + + /// + /// The maximum number of Huffman table classes + /// + internal const int MaxTc = 1; + + /// + /// The maximum number of Huffman table identifiers + /// + internal const int MaxTh = 3; + + internal const int ThRowSize = MaxTh + 1; } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegPixelArea.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegPixelArea.cs index 0161542d8..92d0f3429 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegPixelArea.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegPixelArea.cs @@ -23,8 +23,6 @@ namespace ImageSharp.Formats.Jpg public static JpegPixelArea CreatePooled(int width, int height) { int size = width * height; - //var pixels = ArrayPool.Shared.Rent(size); - //Array.Clear(pixels, 0, size); var pixels = CleanPooler.RentCleanArray(size); return new JpegPixelArea(pixels, width, 0); } @@ -61,7 +59,7 @@ namespace ImageSharp.Formats.Jpg public int Offset { get; private set; } /// - /// Get the subarea that belongs to the given block indices + /// Get the subarea that belongs to the Block8x8 defined by block indices /// /// The block X index /// The block Y index diff --git a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs index 08fcb1018..9e5990d2d 100644 --- a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs @@ -17,38 +17,11 @@ namespace ImageSharp.Formats /// internal unsafe class JpegDecoderCore : IDisposable { - /// - /// The maximum (inclusive) number of bits in a Huffman code. - /// - internal const int MaxCodeLength = 16; - - /// - /// The maximum (inclusive) number of codes in a Huffman tree. - /// - internal const int MaxNCodes = 256; - - /// - /// The log-2 size of the Huffman decoder's look-up table. - /// - internal const int LutSize = 8; - /// /// The maximum number of color components /// private const int MaxComponents = 4; - /// - /// The maximum number of Huffman table classes - /// - private const int MaxTc = 1; - - /// - /// The maximum number of Huffman table identifiers - /// - private const int MaxTh = 3; - - private const int ThRowSize = MaxTh + 1; - /// /// The maximum number of quantization tables /// @@ -87,7 +60,7 @@ namespace ImageSharp.Formats /// /// A temporary buffer for holding pixels /// - private readonly byte[] temp; + private readonly byte[] temp; // TODO: the usage of this buffer is unclean + need to move it to the stack for performance /// /// The byte buffer. @@ -179,24 +152,13 @@ namespace ImageSharp.Formats /// public JpegDecoderCore() { - // this.huffmanTrees = new Huffman[MaxTc + 1, MaxTh + 1]; - this.huffmanTrees = new HuffmanTree[(MaxTc + 1) * (MaxTh + 1)]; - + this.huffmanTrees = HuffmanTree.CreateHuffmanTrees(); this.quantizationTables = new Block8x8F[MaxTq + 1]; this.temp = new byte[2 * Block8x8F.ScalarCount]; this.componentArray = new Component[MaxComponents]; this.progCoeffs = new Block8x8F[MaxComponents][]; this.bits = default(Bits); this.bytes = Bytes.Create(); - - // TODO: This looks like it could be static. - for (int i = 0; i < MaxTc + 1; i++) - { - for (int j = 0; j < MaxTh + 1; j++) - { - this.huffmanTrees[(i * ThRowSize) + j].Init(LutSize, MaxNCodes, MaxCodeLength); - } - } } /// @@ -528,108 +490,22 @@ namespace ImageSharp.Formats this.ReadFull(this.temp, 0, 17); int tc = this.temp[0] >> 4; - if (tc > MaxTc) + if (tc > HuffmanTree.MaxTc) { throw new ImageFormatException("Bad Tc value"); } int th = this.temp[0] & 0x0f; - if (th > MaxTh || (!this.isProgressive && (th > 1))) + if (th > HuffmanTree.MaxTh || (!this.isProgressive && (th > 1))) { throw new ImageFormatException("Bad Th value"); } - this.ProcessDefineHuffmanTablesMarkerLoop(ref this.huffmanTrees[(tc * ThRowSize) + th], ref remaining); + int huffTreeIndex = (tc * HuffmanTree.ThRowSize) + th; + this.huffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(this, this.temp, ref remaining); } } - private void ProcessDefineHuffmanTablesMarkerLoop(ref HuffmanTree huffmanTree, ref int remaining) - { - // Read nCodes and huffman.Valuess (and derive h.Length). - // nCodes[i] is the number of codes with code length i. - // h.Length is the total number of codes. - huffmanTree.Length = 0; - - int[] ncodes = new int[MaxCodeLength]; - for (int i = 0; i < ncodes.Length; i++) - { - ncodes[i] = this.temp[i + 1]; - huffmanTree.Length += ncodes[i]; - } - - if (huffmanTree.Length == 0) - { - throw new ImageFormatException("Huffman table has zero length"); - } - - if (huffmanTree.Length > MaxNCodes) - { - throw new ImageFormatException("Huffman table has excessive length"); - } - - remaining -= huffmanTree.Length + 17; - if (remaining < 0) - { - throw new ImageFormatException("DHT has wrong length"); - } - - this.ReadFull(huffmanTree.Values, 0, huffmanTree.Length); - - // Derive the look-up table. - for (int i = 0; i < huffmanTree.Lut.Length; i++) - { - huffmanTree.Lut[i] = 0; - } - - uint x = 0, code = 0; - - for (int i = 0; i < LutSize; i++) - { - code <<= 1; - - for (int j = 0; j < ncodes[i]; j++) - { - // The codeLength is 1+i, so shift code by 8-(1+i) to - // calculate the high bits for every 8-bit sequence - // whose codeLength's high bits matches code. - // 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)); - ushort lutValue = (ushort)((huffmanTree.Values[x] << 8) | (2 + i)); - - for (int k = 0; k < 1 << (7 - i); k++) - { - huffmanTree.Lut[base2 | k] = lutValue; - } - - code++; - x++; - } - } - - // Derive minCodes, maxCodes, and indices. - int c = 0, index = 0; - for (int i = 0; i < ncodes.Length; i++) - { - int nc = ncodes[i]; - if (nc == 0) - { - huffmanTree.MinCodes[i] = -1; - huffmanTree.MaxCodes[i] = -1; - huffmanTree.Indices[i] = -1; - } - else - { - huffmanTree.MinCodes[i] = c; - huffmanTree.MaxCodes[i] = c + nc - 1; - huffmanTree.Indices[i] = index; - c += nc; - index += nc; - } - - c <<= 1; - } - } /// /// Returns the next Huffman-coded value from the bit-stream, decoded according to the given value. @@ -650,7 +526,7 @@ namespace ImageSharp.Formats if (errorCode == ErrorCodes.NoError) { - ushort v = huffmanTree.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - LutSize)) & 0xff]; + ushort v = huffmanTree.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - HuffmanTree.LutSize)) & 0xff]; if (v != 0) { @@ -667,7 +543,7 @@ namespace ImageSharp.Formats } int code = 0; - for (int i = 0; i < MaxCodeLength; i++) + for (int i = 0; i < HuffmanTree.MaxCodeLength; i++) { if (this.bits.UnreadBits == 0) { @@ -766,7 +642,7 @@ namespace ImageSharp.Formats /// The data to write to. /// The offset in the source buffer /// The number of bytes to read - private void ReadFull(byte[] data, int offset, int length) + internal void ReadFull(byte[] data, int offset, int length) { // Unread the overshot bytes, if any. if (this.bytes.UnreadableBytes != 0) @@ -1710,7 +1586,7 @@ namespace ImageSharp.Formats int bx, Block8x8F* qt) { - int huffmannIdx = (AcTable * ThRowSize) + scan[i].AcTableSelector; + int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector; if (ah != 0) { this.Refine(b, ref this.huffmanTrees[huffmannIdx], unzigPtr, zigStart, zigEnd, 1 << al); @@ -1724,7 +1600,7 @@ namespace ImageSharp.Formats // Decode the DC coefficient, as specified in section F.2.2.1. byte value = this.DecodeHuffman( - ref this.huffmanTrees[(DcTable * ThRowSize) + scan[i].DcTableSelector]); + ref this.huffmanTrees[(DcTable * HuffmanTree.ThRowSize) + scan[i].DcTableSelector]); if (value > 16) { throw new ImageFormatException("Excessive DC component"); @@ -1884,13 +1760,13 @@ namespace ImageSharp.Formats totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; currentScan.DcTableSelector = (byte)(this.temp[2 + (2 * i)] >> 4); - if (currentScan.DcTableSelector > MaxTh) + if (currentScan.DcTableSelector > HuffmanTree.MaxTh) { throw new ImageFormatException("Bad DC table selector value"); } currentScan.AcTableSelector = (byte)(this.temp[2 + (2 * i)] & 0x0f); - if (currentScan.AcTableSelector > MaxTh) + if (currentScan.AcTableSelector > HuffmanTree.MaxTh) { throw new ImageFormatException("Bad AC table selector value"); }