Browse Source

Refactor buffer-filling to be more like libjpegturbo + add optimization notes

af/merge-core
James Jackson-South 8 years ago
parent
commit
e968a4e622
  1. 94
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs

94
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs

@ -122,6 +122,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
// Find marker
this.bitsCount = 0;
this.bitsData = 0;
fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream);
// Some bad images seem to pad Scan blocks with e.g. zero bytes, skip past
@ -433,49 +434,98 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ReadBit(DoubleBufferedStreamReader stream)
{
// TODO: I wonder if we can do this two bytes at a time; libjpeg turbo seems to do that?
if (this.bitsCount > 0)
if (this.bitsCount == 0)
{
this.bitsCount--;
return (this.bitsData >> this.bitsCount) & 1;
this.FillBits(stream);
}
this.bitsData = stream.ReadByte();
this.bitsCount--;
return (this.bitsData >> this.bitsCount) & 1;
}
if (this.bitsData == -0x1)
[MethodImpl(MethodImplOptions.NoInlining)]
private void FillBits(DoubleBufferedStreamReader stream)
{
// TODO: Read more then 1 byte at a time.
// In LibJpegTurbo this is be 25 bits (32-7) but I cannot get this to work
// for some images, I'm assuming because I am crossing MCU boundaries and not managing
// to detect it.
const int MinGetBits = 7;
// Attempt to load to the minimum bit count.
while (this.bitsCount < MinGetBits)
{
// We've encountered the end of the file stream which means there's no EOI marker ref the image
this.endOfStreamReached = true;
}
int c = stream.ReadByte();
if (this.bitsData == PdfJsJpegConstants.Markers.Prefix)
{
int nextByte = stream.ReadByte();
if (nextByte != 0)
if (c == -0x1)
{
// We've encountered the end of the file stream which means there's no EOI marker in the image.
this.endOfStreamReached = true;
// Fill buffer with zero bits.
this.bitsData <<= MinGetBits - this.bitsCount;
this.bitsCount = MinGetBits;
break;
}
if (c == PdfJsJpegConstants.Markers.Prefix)
{
int nextByte = stream.ReadByte();
if (nextByte != 0)
{
#if DEBUG
Debug.WriteLine($"DecodeScan - Unexpected marker {(this.bitsData << 8) | nextByte:X} at {stream.Position}");
Debug.WriteLine($"DecodeScan - Unexpected marker {(c << 8) | nextByte:X} at {stream.Position}");
#endif
// We've encountered an unexpected marker. Reverse the stream and exit.
this.unexpectedMarkerReached = true;
stream.Position -= 2;
// We've encountered an unexpected marker. Reverse the stream and exit.
this.unexpectedMarkerReached = true;
stream.Position -= 2;
// Fill buffer with zero bits.
this.bitsData <<= MinGetBits - this.bitsCount;
this.bitsCount = MinGetBits;
break;
}
}
// Unstuff 0
// OK, load c into get_buffer
this.bitsData = (this.bitsData << 8) | c;
this.bitsCount += 8;
}
}
this.bitsCount = 7;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int PeekBits(int count)
{
return this.bitsData >> (this.bitsCount - count) & ((1 << count) - 1);
}
return this.bitsData >> 7;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DropBits(int count)
{
this.bitsCount -= count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private short DecodeHuffman(ref PdfJsHuffmanTable tree, DoubleBufferedStreamReader stream)
{
// 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.
// In LibJpegTurbo a minimum of 25 bits (32-7) is collected from the stream
// Then a LUT is used to avoid the loop when decoding the Huffman value.
// using 3 methods: FillBits, PeekBits, and DropBits.
// The LUT has been ported from LibJpegTurbo as has this code but it doesn't work.
// this.FillBits(stream);
//
// const int LookAhead = 8;
// int look = this.PeekBits(LookAhead);
// look = tree.Lookahead[look];
// int bits = look >> LookAhead;
//
// if (bits <= LookAhead)
// {
// this.DropBits(bits);
// return (short)(look & ((1 << LookAhead) - 1));
// }
short code = (short)this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{

Loading…
Cancel
Save