diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderBase.cs b/src/ImageSharp/Formats/WebP/WebPDecoderBase.cs index 9779b2dfe8..b9c82c7ecb 100644 --- a/src/ImageSharp/Formats/WebP/WebPDecoderBase.cs +++ b/src/ImageSharp/Formats/WebP/WebPDecoderBase.cs @@ -27,6 +27,30 @@ namespace SixLabors.ImageSharp.Formats.WebP return huffmanImageSpan[(xSize * (y >> bits)) + (x >> bits)]; } + /// + /// Decodes the next Huffman code from bit-stream. + /// FillBitWindow(br) needs to be called at minimum every second call + /// to ReadSymbol, in order to pre-fetch enough bits. + /// + protected uint ReadSymbol(Span table, Vp8LBitReader bitReader) + { + // TODO: if the bitReader field is moved to this base class we could omit the parameter. + uint val = (uint)bitReader.PrefetchBits(); + Span tableSpan = table.Slice((int)(val & HuffmanUtils.HuffmanTableMask)); + int nBits = tableSpan[0].BitsUsed - HuffmanUtils.HuffmanTableBits; + if (nBits > 0) + { + bitReader.AdvanceBitPosition(HuffmanUtils.HuffmanTableBits); + val = (uint)bitReader.PrefetchBits(); + tableSpan = tableSpan.Slice((int)tableSpan[0].Value); + tableSpan = tableSpan.Slice((int)val & ((1 << nBits) - 1)); + } + + bitReader.AdvanceBitPosition(tableSpan[0].BitsUsed); + + return tableSpan[0].Value; + } + protected int GetCopyDistance(int distanceSymbol, Vp8LBitReader bitReader) { if (distanceSymbol < 4) diff --git a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs index 40f13aaea3..ab4dd63069 100644 --- a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs @@ -235,7 +235,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } else { - code = (int)this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Green]); + code = (int)this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Green], this.bitReader); } if (this.bitReader.IsEndOfStream()) @@ -252,10 +252,10 @@ namespace SixLabors.ImageSharp.Formats.WebP } else { - uint red = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Red]); + uint red = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Red], this.bitReader); this.bitReader.FillBitWindow(); - uint blue = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Blue]); - uint alpha = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Alpha]); + uint blue = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Blue], this.bitReader); + uint alpha = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Alpha], this.bitReader); if (this.bitReader.IsEndOfStream()) { break; @@ -272,7 +272,7 @@ namespace SixLabors.ImageSharp.Formats.WebP // Backward reference is used. int lengthSym = code - WebPConstants.NumLiteralCodes; int length = this.GetCopyLength(lengthSym, this.bitReader); - uint distSymbol = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Dist]); + uint distSymbol = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Dist], this.bitReader); this.bitReader.FillBitWindow(); int distCode = this.GetCopyDistance((int)distSymbol, this.bitReader); int dist = this.PlaneCodeToDistance(width, distCode); @@ -691,29 +691,6 @@ namespace SixLabors.ImageSharp.Formats.WebP decoder.Metadata.HuffmanMask = (numBits is 0) ? ~0 : (1 << numBits) - 1; } - /// - /// Decodes the next Huffman code from bit-stream. - /// FillBitWindow(br) needs to be called at minimum every second call - /// to ReadSymbol, in order to pre-fetch enough bits. - /// - private uint ReadSymbol(Span table) - { - uint val = (uint)this.bitReader.PrefetchBits(); - Span tableSpan = table.Slice((int)(val & HuffmanUtils.HuffmanTableMask)); - int nBits = tableSpan[0].BitsUsed - HuffmanUtils.HuffmanTableBits; - if (nBits > 0) - { - this.bitReader.AdvanceBitPosition(HuffmanUtils.HuffmanTableBits); - val = (uint)this.bitReader.PrefetchBits(); - tableSpan = tableSpan.Slice((int)tableSpan[0].Value); - tableSpan = tableSpan.Slice((int)val & ((1 << nBits) - 1)); - } - - this.bitReader.AdvanceBitPosition(tableSpan[0].BitsUsed); - - return tableSpan[0].Value; - } - private uint ReadPackedSymbols(HTreeGroup[] group, Span pixelData, int decodedPixels) { uint val = (uint)(this.bitReader.PrefetchBits() & (HuffmanUtils.HuffmanPackedTableSize - 1));