From d525f662b553acd764b17bd93d7decfad12fc426 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 1 Nov 2019 21:20:28 +0100 Subject: [PATCH] Add ReadHuffmanCodeLengths --- src/ImageSharp/Formats/WebP/WebPConstants.cs | 12 +++ .../Formats/WebP/WebPLosslessDecoder.cs | 97 ++++++++++++++++++- 2 files changed, 104 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/WebP/WebPConstants.cs b/src/ImageSharp/Formats/WebP/WebPConstants.cs index 7bd1f559b6..a2930171db 100644 --- a/src/ImageSharp/Formats/WebP/WebPConstants.cs +++ b/src/ImageSharp/Formats/WebP/WebPConstants.cs @@ -76,6 +76,8 @@ namespace SixLabors.ImageSharp.Formats.WebP public static int MaxAllowedCodeLength = 15; + public static int DefaultCodeLength = 8; + public static int HuffmanCodesPerMetaCode = 5; public static int NumLiteralCodes = 256; @@ -86,6 +88,16 @@ namespace SixLabors.ImageSharp.Formats.WebP public static int NumCodeLengthCodes = 19; + public static int LengthTableBits = 7; + + public static int kCodeLengthLiterals = 16; + + public static int kCodeLengthRepeatCode = 16; + + public static int[] kCodeLengthExtraBits = { 2, 3, 7 }; + + public static int[] kCodeLengthRepeatOffsets = { 3, 3, 11 }; + public static byte[] KCodeLengthCodeOrder = { 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; public static int[] kAlphabetSize = { diff --git a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs index 7a2c55b371..2b90ee060c 100644 --- a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs @@ -101,10 +101,10 @@ namespace SixLabors.ImageSharp.Formats.WebP { alphabetSize += 1 << colorCacheBits; size = this.ReadHuffmanCode(alphabetSize, codeLengths); - if (size is 0) + /*if (size is 0) { WebPThrowHelper.ThrowImageFormatException("Huffman table size is zero"); - } + }*/ } } } @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Formats.WebP codeLengthCodeLengths[WebPConstants.KCodeLengthCodeOrder[i]] = (int)this.bitReader.Read(3); } - // TODO: ReadHuffmanCodeLengths + this.ReadHuffmanCodeLengths(codeLengthCodeLengths, alphabetSize, codeLengths); } int size = 0; @@ -160,9 +160,66 @@ namespace SixLabors.ImageSharp.Formats.WebP return size; } - private int BuildHuffmanTable(int rootBits, int[] codeLengths, int codeLengthsSize, - int[] sorted) // sorted[code_lengths_size] is a pre-allocated array for sorting symbols by code length. + private void ReadHuffmanCodeLengths(int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths) + { + int maxSymbol; + int symbol = 0; + int prevCodeLen = WebPConstants.DefaultCodeLength; + BuildHuffmanTable(WebPConstants.LengthTableBits, codeLengthCodeLengths, WebPConstants.NumCodeLengthCodes); + if (this.bitReader.ReadBit()) + { + int lengthNBits = 2 + (2 * (int)this.bitReader.Read(3)); + maxSymbol = 2 + (int)this.bitReader.Read(lengthNBits); + } + else + { + maxSymbol = numSymbols; + } + + while (symbol < numSymbols) + { + int codeLen; + if (maxSymbol-- == 0) + { + break; + } + + codeLen = int.MaxValue; // TODO: this is wrong + if (codeLen < WebPConstants.kCodeLengthLiterals) + { + codeLengths[symbol++] = codeLen; + if (codeLen != 0) + { + prevCodeLen = codeLen; + } + } + else + { + bool usePrev = codeLen == WebPConstants.kCodeLengthRepeatCode; + int slot = codeLen - WebPConstants.kCodeLengthLiterals; + int extraBits = WebPConstants.kCodeLengthExtraBits[slot]; + int repeatOffset = WebPConstants.kCodeLengthRepeatOffsets[slot]; + int repeat = (int)(this.bitReader.Read(extraBits) + repeatOffset); + if (symbol + repeat > numSymbols) + { + return; + } + else + { + int length = usePrev ? prevCodeLen : 0; + while (repeat-- > 0) + { + codeLengths[symbol++] = length; + } + } + } + } + } + + private int BuildHuffmanTable(int rootBits, int[] codeLengths, int codeLengthsSize) { + // sorted[code_lengths_size] is a pre-allocated array for sorting symbols by code length. + var sorted = new int[codeLengthsSize]; // total size root table + 2nd level table int totalSize = 1 << rootBits; // current code length @@ -219,6 +276,36 @@ namespace SixLabors.ImageSharp.Formats.WebP 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) + { + numOpen <<= 1; + numNodes += numOpen; + numOpen -= count[len]; + if (numOpen < 0) + { + return 0; + } + + for (; count[len] > 0; --count[len]) + { + var code = new HuffmanCode() + { + BitsUsed = len, + Value = sorted[symbol++] + }; + } + } + return 0; }