/// <summary> /// Stores code in table[0], table[step], table[2*step], ..., table[end-step]. /// Assumes that end is an integer multiple of step. /// </summary> private static void ReplicateValue(Span <HuffmanCode> table, int step, int end, HuffmanCode code) { DebugGuard.IsTrue(end % step == 0, nameof(end), "end must be a multiple of step"); do { end -= step; table[end] = code; }while (end > 0); }
private uint ReadPackedSymbols(Span <HTreeGroup> group, Span <uint> pixelData, int decodedPixels) { uint val = (uint)(this.bitReader.PrefetchBits() & (HuffmanUtils.HuffmanPackedTableSize - 1)); HuffmanCode code = group[0].PackedTable[val]; if (code.BitsUsed < BitsSpecialMarker) { this.bitReader.AdvanceBitPosition(code.BitsUsed); pixelData[decodedPixels] = code.Value; return(PackedNonLiteralCode); } this.bitReader.AdvanceBitPosition(code.BitsUsed - BitsSpecialMarker); return(code.Value); }
private void BuildPackedTable(HTreeGroup hTreeGroup) { for (uint code = 0; code < HuffmanUtils.HuffmanPackedTableSize; code++) { uint bits = code; ref HuffmanCode huff = ref hTreeGroup.PackedTable[bits]; HuffmanCode hCode = hTreeGroup.HTrees[HuffIndex.Green][bits]; if (hCode.Value >= WebpConstants.NumLiteralCodes) { huff.BitsUsed = hCode.BitsUsed + BitsSpecialMarker; huff.Value = hCode.Value; } else { huff.BitsUsed = 0; huff.Value = 0; bits >>= AccumulateHCode(hCode, 8, ref huff); bits >>= AccumulateHCode(hTreeGroup.HTrees[HuffIndex.Red][bits], 16, ref huff); bits >>= AccumulateHCode(hTreeGroup.HTrees[HuffIndex.Blue][bits], 0, ref huff); bits >>= AccumulateHCode(hTreeGroup.HTrees[HuffIndex.Alpha][bits], 24, ref huff); } }
private void ReadHuffmanCodeLengths(Span <HuffmanCode> table, int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths) { int maxSymbol; int symbol = 0; int prevCodeLen = WebpConstants.DefaultCodeLength; int size = HuffmanUtils.BuildHuffmanTable(table, WebpConstants.LengthTableBits, codeLengthCodeLengths, NumCodeLengthCodes); if (size == 0) { WebpThrowHelper.ThrowImageFormatException("Error building huffman table"); } if (this.bitReader.ReadBit()) { int lengthNBits = 2 + (2 * (int)this.bitReader.ReadValue(3)); maxSymbol = 2 + (int)this.bitReader.ReadValue(lengthNBits); } else { maxSymbol = numSymbols; } while (symbol < numSymbols) { if (maxSymbol-- == 0) { break; } this.bitReader.FillBitWindow(); ulong prefetchBits = this.bitReader.PrefetchBits(); int idx = (int)(prefetchBits & 127); HuffmanCode huffmanCode = table[idx]; this.bitReader.AdvanceBitPosition(huffmanCode.BitsUsed); uint codeLen = huffmanCode.Value; if (codeLen < WebpConstants.CodeLengthLiterals) { codeLengths[symbol++] = (int)codeLen; if (codeLen != 0) { prevCodeLen = (int)codeLen; } } else { bool usePrev = codeLen == WebpConstants.CodeLengthRepeatCode; uint slot = codeLen - WebpConstants.CodeLengthLiterals; int extraBits = WebpConstants.CodeLengthExtraBits[slot]; int repeatOffset = WebpConstants.CodeLengthRepeatOffsets[slot]; int repeat = (int)(this.bitReader.ReadValue(extraBits) + repeatOffset); if (symbol + repeat > numSymbols) { return; } int length = usePrev ? prevCodeLen : 0; while (repeat-- > 0) { codeLengths[symbol++] = length; } } } }
private void ReadHuffmanCodes(Vp8LDecoder decoder, int xSize, int ySize, int colorCacheBits, bool allowRecursion) { int maxAlphabetSize = 0; int numHTreeGroups = 1; int numHTreeGroupsMax = 1; // If the next bit is zero, there is only one meta Huffman code used everywhere in the image. No more data is stored. // If this bit is one, the image uses multiple meta Huffman codes. These meta Huffman codes are stored as an entropy image. if (allowRecursion && this.bitReader.ReadBit()) { // Use meta Huffman codes. int huffmanPrecision = (int)(this.bitReader.ReadValue(3) + 2); int huffmanXSize = LosslessUtils.SubSampleSize(xSize, huffmanPrecision); int huffmanYSize = LosslessUtils.SubSampleSize(ySize, huffmanPrecision); int huffmanPixels = huffmanXSize * huffmanYSize; IMemoryOwner <uint> huffmanImage = this.DecodeImageStream(decoder, huffmanXSize, huffmanYSize, false); Span <uint> huffmanImageSpan = huffmanImage.GetSpan(); decoder.Metadata.HuffmanSubSampleBits = huffmanPrecision; // TODO: Isn't huffmanPixels the length of the span? for (int i = 0; i < huffmanPixels; i++) { // The huffman data is stored in red and green bytes. uint group = (huffmanImageSpan[i] >> 8) & 0xffff; huffmanImageSpan[i] = group; if (group >= numHTreeGroupsMax) { numHTreeGroupsMax = (int)group + 1; } } numHTreeGroups = numHTreeGroupsMax; decoder.Metadata.HuffmanImage = huffmanImage; } // Find maximum alphabet size for the hTree group. for (int j = 0; j < WebpConstants.HuffmanCodesPerMetaCode; j++) { int alphabetSize = WebpConstants.AlphabetSize[j]; if (j == 0 && colorCacheBits > 0) { alphabetSize += 1 << colorCacheBits; } if (maxAlphabetSize < alphabetSize) { maxAlphabetSize = alphabetSize; } } int tableSize = TableSize[colorCacheBits]; var huffmanTables = new HuffmanCode[numHTreeGroups * tableSize]; var hTreeGroups = new HTreeGroup[numHTreeGroups]; Span <HuffmanCode> huffmanTable = huffmanTables.AsSpan(); int[] codeLengths = new int[maxAlphabetSize]; for (int i = 0; i < numHTreeGroupsMax; i++) { hTreeGroups[i] = new HTreeGroup(HuffmanUtils.HuffmanPackedTableSize); HTreeGroup hTreeGroup = hTreeGroups[i]; int totalSize = 0; bool isTrivialLiteral = true; int maxBits = 0; codeLengths.AsSpan().Clear(); for (int j = 0; j < WebpConstants.HuffmanCodesPerMetaCode; j++) { int alphabetSize = WebpConstants.AlphabetSize[j]; if (j == 0 && colorCacheBits > 0) { alphabetSize += 1 << colorCacheBits; } int size = this.ReadHuffmanCode(alphabetSize, codeLengths, huffmanTable); if (size == 0) { WebpThrowHelper.ThrowImageFormatException("Huffman table size is zero"); } // TODO: Avoid allocation. hTreeGroup.HTrees.Add(huffmanTable.Slice(0, size).ToArray()); HuffmanCode huffTableZero = huffmanTable[0]; if (isTrivialLiteral && LiteralMap[j] == 1) { isTrivialLiteral = huffTableZero.BitsUsed == 0; } totalSize += huffTableZero.BitsUsed; huffmanTable = huffmanTable.Slice(size); if (j <= HuffIndex.Alpha) { int localMaxBits = codeLengths[0]; int k; for (k = 1; k < alphabetSize; ++k) { int codeLengthK = codeLengths[k]; if (codeLengthK > localMaxBits) { localMaxBits = codeLengthK; } } maxBits += localMaxBits; } } hTreeGroup.IsTrivialLiteral = isTrivialLiteral; hTreeGroup.IsTrivialCode = false; if (isTrivialLiteral) { uint red = hTreeGroup.HTrees[HuffIndex.Red][0].Value; uint blue = hTreeGroup.HTrees[HuffIndex.Blue][0].Value; uint green = hTreeGroup.HTrees[HuffIndex.Green][0].Value; uint alpha = hTreeGroup.HTrees[HuffIndex.Alpha][0].Value; hTreeGroup.LiteralArb = (alpha << 24) | (red << 16) | blue; if (totalSize == 0 && green < WebpConstants.NumLiteralCodes) { hTreeGroup.IsTrivialCode = true; hTreeGroup.LiteralArb |= green << 8; } } hTreeGroup.UsePackedTable = !hTreeGroup.IsTrivialCode && maxBits < HuffmanUtils.HuffmanPackedBits; if (hTreeGroup.UsePackedTable) { this.BuildPackedTable(hTreeGroup); } } decoder.Metadata.NumHTreeGroups = numHTreeGroups; decoder.Metadata.HTreeGroups = hTreeGroups; decoder.Metadata.HuffmanTables = huffmanTables; }
public static int BuildHuffmanTable(Span <HuffmanCode> table, int rootBits, int[] codeLengths, int codeLengthsSize) { DebugGuard.MustBeGreaterThan(rootBits, 0, nameof(rootBits)); DebugGuard.NotNull(codeLengths, nameof(codeLengths)); DebugGuard.MustBeGreaterThan(codeLengthsSize, 0, nameof(codeLengthsSize)); // sorted[codeLengthsSize] is a pre-allocated array for sorting symbols by code length. int[] sorted = new int[codeLengthsSize]; int totalSize = 1 << rootBits; // total size root table + 2nd level table. int len; // current code length. int symbol; // symbol index in original or sorted table. int[] counts = new int[WebpConstants.MaxAllowedCodeLength + 1]; // number of codes of each length. int[] offsets = new int[WebpConstants.MaxAllowedCodeLength + 1]; // offsets in sorted table for each length. // Build histogram of code lengths. for (symbol = 0; symbol < codeLengthsSize; ++symbol) { int codeLengthOfSymbol = codeLengths[symbol]; if (codeLengthOfSymbol > WebpConstants.MaxAllowedCodeLength) { return(0); } counts[codeLengthOfSymbol]++; } // Error, all code lengths are zeros. if (counts[0] == codeLengthsSize) { return(0); } // Generate offsets into sorted symbol table by code length. offsets[1] = 0; for (len = 1; len < WebpConstants.MaxAllowedCodeLength; ++len) { int codesOfLength = counts[len]; if (codesOfLength > 1 << len) { return(0); } offsets[len + 1] = offsets[len] + codesOfLength; } // Sort symbols by length, by symbol order within each length. for (symbol = 0; symbol < codeLengthsSize; ++symbol) { int symbolCodeLength = codeLengths[symbol]; if (symbolCodeLength > 0) { sorted[offsets[symbolCodeLength]++] = symbol; } } // Special case code with only one value. if (offsets[WebpConstants.MaxAllowedCodeLength] == 1) { var huffmanCode = new HuffmanCode() { BitsUsed = 0, Value = (uint)sorted[0] }; ReplicateValue(table, 1, totalSize, huffmanCode); return(totalSize); } int step; // step size to replicate values in current table int low = -1; // low bits for current root entry int mask = totalSize - 1; // mask for low bits int key = 0; // reversed prefix code int numNodes = 1; // number of Huffman tree nodes int numOpen = 1; // number of open branches in current tree level int tableBits = rootBits; // key length of current table int tableSize = 1 << tableBits; // size of current table symbol = 0; // Fill in root table. for (len = 1, step = 2; len <= rootBits; ++len, step <<= 1) { int countsLen = counts[len]; numOpen <<= 1; numNodes += numOpen; numOpen -= counts[len]; if (numOpen < 0) { return(0); } for (; countsLen > 0; countsLen--) { var huffmanCode = new HuffmanCode() { BitsUsed = len, Value = (uint)sorted[symbol++] }; ReplicateValue(table.Slice(key), step, tableSize, huffmanCode); key = GetNextKey(key, len); } counts[len] = countsLen; } // Fill in 2nd level tables and add pointers to root table. Span <HuffmanCode> tableSpan = table; int tablePos = 0; for (len = rootBits + 1, step = 2; len <= WebpConstants.MaxAllowedCodeLength; ++len, step <<= 1) { numOpen <<= 1; numNodes += numOpen; numOpen -= counts[len]; if (numOpen < 0) { return(0); } for (; counts[len] > 0; --counts[len]) { if ((key & mask) != low) { tableSpan = tableSpan.Slice(tableSize); tablePos += tableSize; tableBits = NextTableBitSize(counts, len, rootBits); tableSize = 1 << tableBits; totalSize += tableSize; low = key & mask; table[low] = new HuffmanCode { BitsUsed = tableBits + rootBits, Value = (uint)(tablePos - low) }; } var huffmanCode = new HuffmanCode { BitsUsed = len - rootBits, Value = (uint)sorted[symbol++] }; ReplicateValue(tableSpan.Slice(key >> rootBits), step, tableSize, huffmanCode); key = GetNextKey(key, len); } } return(totalSize); }