// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Formats.Jpg { using System; using System.Buffers; /// /// Represents a Huffman tree /// internal struct HuffmanTree : IDisposable { /// /// The maximum (inclusive) number of codes in a Huffman tree. /// public const int MaxNCodes = 256; /// /// The maximum (inclusive) number of bits in a Huffman code. /// public const int MaxCodeLength = 16; /// /// The maximum number of Huffman table classes /// public const int MaxTc = 1; /// /// The maximum number of Huffman table identifiers /// public const int MaxTh = 3; /// /// Row size of the Huffman table /// public const int ThRowSize = MaxTh + 1; /// /// Number of Hufman Trees in the Huffman table /// public const int NumberOfTrees = (MaxTc + 1) * (MaxTh + 1); /// /// The log-2 size of the Huffman decoder's look-up table. /// public const int LutSizeLog2 = 8; /// /// Gets or sets the number of codes in the tree. /// public int Length; /// /// Gets the look-up table for the next LutSize bits in the bit-stream. /// The high 8 bits of the uint16 are the encoded value. The low 8 bits /// are 1 plus the code length, or 0 if the value is too large to fit in /// lutSize bits. /// public int[] Lut; /// /// Gets the the decoded values, sorted by their encoding. /// public int[] Values; /// /// Gets the array of minimum codes. /// MinCodes[i] is the minimum code of length i, or -1 if there are no codes of that length. /// public int[] MinCodes; /// /// Gets the array of maximum codes. /// MaxCodes[i] is the maximum code of length i, or -1 if there are no codes of that length. /// public int[] MaxCodes; /// /// Gets the array of indices. Indices[i] is the index into Values of MinCodes[i]. /// public int[] Indices; private static readonly ArrayPool IntPool256 = ArrayPool.Create(MaxNCodes, 50); private static readonly ArrayPool BytePool256 = ArrayPool.Create(MaxNCodes, 50); private static readonly ArrayPool CodesPool16 = ArrayPool.Create(MaxCodeLength, 50); /// /// Creates and initializes an array of instances of size /// /// An array of instances representing the Huffman tables public static HuffmanTree[] CreateHuffmanTrees() { HuffmanTree[] result = new HuffmanTree[NumberOfTrees]; for (int i = 0; i < MaxTc + 1; i++) { for (int j = 0; j < MaxTh + 1; j++) { result[(i * ThRowSize) + j].Init(); } } return result; } /// /// Disposes the underlying buffers /// public void Dispose() { IntPool256.Return(this.Lut, true); IntPool256.Return(this.Values, true); CodesPool16.Return(this.MinCodes, true); CodesPool16.Return(this.MaxCodes, true); CodesPool16.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 the Jpeg stream /// Remaining bits public void ProcessDefineHuffmanTablesMarkerLoop( ref InputProcessor inputProcessor, 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"); } byte[] values = null; try { values = BytePool256.Rent(MaxNCodes); inputProcessor.ReadFull(values, 0, this.Length); for (int i = 0; i < values.Length; i++) { this.Values[i] = values[i]; } } finally { BytePool256.Return(values, true); } // Derive the look-up table. for (int i = 0; i < this.Lut.Length; i++) { this.Lut[i] = 0; } int x = 0, code = 0; for (int i = 0; i < LutSizeLog2; 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. int base2 = code << (7 - i); int lutValue = (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; } } /// /// Gets the value for the given code and index. /// /// The code /// The code length /// The value public int GetValue(int code, int codeLength) { return this.Values[this.Indices[codeLength] + code - this.MinCodes[codeLength]]; } /// /// Initializes the Huffman tree /// private void Init() { this.Lut = IntPool256.Rent(MaxNCodes); this.Values = IntPool256.Rent(MaxNCodes); this.MinCodes = CodesPool16.Rent(MaxCodeLength); this.MaxCodes = CodesPool16.Rent(MaxCodeLength); this.Indices = CodesPool16.Rent(MaxCodeLength); } } }