diff --git a/src/ImageSharp/Formats/WebP/HuffmanUtils.cs b/src/ImageSharp/Formats/WebP/HuffmanUtils.cs index b3c125df3..5ae3d4e67 100644 --- a/src/ImageSharp/Formats/WebP/HuffmanUtils.cs +++ b/src/ImageSharp/Formats/WebP/HuffmanUtils.cs @@ -125,18 +125,22 @@ namespace SixLabors.ImageSharp.Formats.WebP } Span tableSpan = table; + int tablePos = 0; for (; count[len] > 0; --count[len]) { if ((key & mask) != low) { tableSpan = tableSpan.Slice(tableSize); + tablePos += tableSize; tableBits = NextTableBitSize(count, len, rootBits); tableSize = 1 << tableBits; totalSize += tableSize; low = key & mask; - table[low].BitsUsed = tableBits + rootBits; - // TODO: fix this - // table[low].Value = ((table - rootTable) - low); + table[low] = new HuffmanCode + { + BitsUsed = tableBits + rootBits, + Value = (uint)(tablePos - low) + }; } var huffmanCode = new HuffmanCode diff --git a/src/ImageSharp/Formats/WebP/Vp8LMetadata.cs b/src/ImageSharp/Formats/WebP/Vp8LMetadata.cs index 0f9595a41..edc72a822 100644 --- a/src/ImageSharp/Formats/WebP/Vp8LMetadata.cs +++ b/src/ImageSharp/Formats/WebP/Vp8LMetadata.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.WebP public int HuffmanXSize { get; set; } - public int[] HuffmanImage { get; set; } + public uint[] HuffmanImage { get; set; } public int NumHTreeGroups { get; set; } diff --git a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs index 1ce5552fe..152efed1f 100644 --- a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs @@ -108,15 +108,19 @@ namespace SixLabors.ImageSharp.Formats.WebP private uint[] DecodeImageStream(int xSize, int ySize, bool isLevel0) { - this.ReadTransformations(); + if (isLevel0) + { + this.ReadTransformations(); + } // Read color cache, if present. bool colorCachePresent = this.bitReader.ReadBit(); int colorCacheBits = 0; int colorCacheSize = 0; - var colorCache = new ColorCache(); + ColorCache colorCache = null; if (colorCachePresent) { + colorCache = new ColorCache(); colorCacheBits = (int)this.bitReader.ReadBits(4); colorCacheSize = 1 << colorCacheBits; if (!(colorCacheBits >= 1 && colorCacheBits <= WebPConstants.MaxColorCacheBits)) @@ -184,7 +188,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } else { - this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Green]); + code = (int)this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Green]); } if (this.bitReader.IsEndOfStream()) @@ -301,22 +305,35 @@ namespace SixLabors.ImageSharp.Formats.WebP private Vp8LMetadata ReadHuffmanCodes(int xSize, int ySize, int colorCacheBits, bool allowRecursion) { + var metadata = new Vp8LMetadata(); 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. - bool isEntropyImage = this.bitReader.ReadBit(); - if (allowRecursion && isEntropyImage) + if (allowRecursion && this.bitReader.ReadBit()) { // Use meta Huffman codes. uint huffmanPrecision = this.bitReader.ReadBits(3) + 2; int huffmanXSize = this.SubSampleSize(xSize, (int)huffmanPrecision); int huffmanYSize = this.SubSampleSize(ySize, (int)huffmanPrecision); int huffmanPixs = huffmanXSize * huffmanYSize; - // TODO: decode entropy image - return new Vp8LMetadata(); + uint[] huffmanImage = this.DecodeImageStream(huffmanXSize, huffmanYSize, false); + metadata.HuffmanSubSampleBits = (int)huffmanPrecision; + for (int i = 0; i < huffmanPixs; ++i) + { + // The huffman data is stored in red and green bytes. + uint group = (huffmanImage[i] >> 8) & 0xffff; + huffmanImage[i] = group; + if (group >= numHTreeGroupsMax) + { + numHTreeGroupsMax = (int)group + 1; + } + } + + numHTreeGroups = numHTreeGroupsMax; + metadata.HuffmanImage = huffmanImage; } // Find maximum alphabet size for the hTree group. @@ -409,13 +426,9 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - var metadata = new Vp8LMetadata() - { - // TODO: initialize huffman_image_ - NumHTreeGroups = numHTreeGroups, - HTreeGroups = hTreeGroups, - HuffmanTables = huffmanTables, - }; + metadata.NumHTreeGroups = numHTreeGroups; + metadata.HTreeGroups = hTreeGroups; + metadata.HuffmanTables = huffmanTables; return metadata; } @@ -476,7 +489,6 @@ namespace SixLabors.ImageSharp.Formats.WebP private void ReadHuffmanCodeLengths(HuffmanCode[] table, int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths) { - Span tableSpan = table.AsSpan(); int maxSymbol; int symbol = 0; int prevCodeLen = WebPConstants.DefaultCodeLength; @@ -498,7 +510,7 @@ namespace SixLabors.ImageSharp.Formats.WebP while (symbol < numSymbols) { - if (maxSymbol-- == 0) + if (maxSymbol-- is 0) { break; } @@ -732,9 +744,9 @@ namespace SixLabors.ImageSharp.Formats.WebP return hCode.BitsUsed; } - private int GetMetaIndex(int[] image, int xSize, int bits, int x, int y) + private uint GetMetaIndex(uint[] image, int xSize, int bits, int x, int y) { - if (bits == 0) + if (bits is 0) { return 0; } @@ -744,8 +756,8 @@ namespace SixLabors.ImageSharp.Formats.WebP private HTreeGroup[] GetHTreeGroupForPos(Vp8LMetadata metadata, int x, int y) { - int metaIndex = this.GetMetaIndex(metadata.HuffmanImage, metadata.HuffmanXSize, metadata.HuffmanSubSampleBits, x, y); - return metadata.HTreeGroups.AsSpan(metaIndex).ToArray(); + uint metaIndex = this.GetMetaIndex(metadata.HuffmanImage, metadata.HuffmanXSize, metadata.HuffmanSubSampleBits, x, y); + return metadata.HTreeGroups.AsSpan((int)metaIndex).ToArray(); } } }