diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs
index c3faa9d1ee..62ae34335e 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
@@ -51,7 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
GenerateSizeTable(lengths, ref huffsizeRef);
GenerateCodeTable(ref huffsizeRef, ref huffcodeRef, length);
this.GenerateDecoderTables(lengths, ref huffcodeRef);
- this.GenerateLookaheadTables(lengths, values);
+ this.GenerateLookaheadTables(lengths, values, ref huffcodeRef);
}
fixed (byte* huffValRef = this.HuffVal.Data)
@@ -145,33 +146,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
///
/// The code lengths
/// The huffman value array
- private void GenerateLookaheadTables(byte[] lengths, byte[] huffval)
+ /// The huffman code span ref
+ private void GenerateLookaheadTables(byte[] lengths, byte[] huffval, ref short huffcodeRef)
{
+ // TODO: This generation code matches the libJpeg code but the lookahead table is not actually used yet.
+ // To use it we need to implement fast lookup path in PdfJsScanDecoder.DecodeHuffman
+ // This should yield much faster scan decoding as usually, more than 95% of the Huffman codes
+ // will be 8 or fewer bits long and can be handled without looping.
fixed (short* lookaheadRef = this.Lookahead.Data)
{
- int x = 0, code = 0;
-
- for (int i = 0; i < 8; i++)
+ for (int i = 0; i < 256; i++)
{
- code <<= 1;
+ lookaheadRef[i] = 2034; // 9 << 8;
+ }
- for (int j = 0; j < lengths[i + 1]; j++)
+ int p = 0;
+ for (int l = 1; l <= 8; l++)
+ {
+ for (int i = 1; i <= lengths[l]; i++, p++)
{
- // 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.
- byte base2 = (byte)(code << (7 - i));
- short lutValue = (short)((short)(huffval[x] << 8) | (short)(2 + i));
-
- for (int k = 0; k < 1 << (7 - i); k++)
+ // l = current code's length, p = its index in huffcode[] & huffval[].
+ // Generate left-justified code followed by all possible bit sequences
+ int lookBits = Unsafe.Add(ref huffcodeRef, p) << (8 - l);
+ for (int ctr = 1 << (8 - l); ctr > 0; ctr--)
{
- lookaheadRef[base2 | k] = lutValue;
+ lookaheadRef[lookBits] = (short)((l << 8) | huffval[p]);
+ lookBits++;
}
-
- code++;
- x++;
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs
index f9320443ac..5fcaa6cea2 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs
@@ -25,12 +25,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
private int bitsCount;
-#pragma warning disable 414
- private int bitsUnRead;
-
- private int accumulator;
-#pragma warning restore 414
-
private int specStart;
private int specEnd;
@@ -128,8 +122,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
// Find marker
this.bitsCount = 0;
- this.accumulator = 0;
- this.bitsUnRead = 0;
fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream);
// Some bad images seem to pad Scan blocks with e.g. zero bytes, skip past
@@ -481,44 +473,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private short DecodeHuffman(ref PdfJsHuffmanTable tree, Stream stream)
{
- short code = -1;
-
- // TODO: Adding this code introduces error into the decoder.
+ // TODO: Implement fast Huffman decoding.
// NOTES # During investigation of the libjpeg implementation it appears that they pull 32bits at a time and operate on those bits
- // using 3 methods: FillBits, PeekBits, and ReadBits. We should attempt to do the same.
- // It doesn't appear to speed anything up either.
- // if (this.bitsUnRead < 8)
- // {
- // if (this.bitsCount <= 0)
- // {
- // code = (short)this.ReadBit(stream);
- // if (this.endOfStreamReached || this.unexpectedMarkerReached)
- // {
- // return -1;
- // }
- //
- // this.bitsUnRead += 8;
- // }
- //
- // this.accumulator = (this.accumulator << 8) | this.bitsData;
- // int lutIndex = (this.accumulator >> (8 - this.bitsUnRead)) & 0xFF;
- // int v = tree.Lookahead[lutIndex];
- // if (v != 0)
- // {
- // int nb = (v & 0xFF) - 1;
- // this.bitsCount -= nb - 1;
- // this.bitsUnRead -= nb;
- // v = v >> 8;
- // return (short)v;
- // }
- // }
- if (code == -1)
+ // using 3 methods: FillBits, PeekBits, and ReadBits. We should attempt to do the same.
+ short code = (short)this.ReadBit(stream);
+ if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
- code = (short)this.ReadBit(stream);
- if (this.endOfStreamReached || this.unexpectedMarkerReached)
- {
- return -1;
- }
+ return -1;
}
// "DECODE", section F.2.2.3, figure F.16, page 109 of T.81