Browse Source

Wire up huffman tables. (doesn't work)

pull/643/head
James Jackson-South 8 years ago
parent
commit
c8a9b30457
  1. 24
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer512.cs
  2. 126
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs
  3. 87
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs
  4. 50
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

24
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer512.cs

@ -0,0 +1,24 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct FixedByteBuffer512
{
public fixed byte Data[1 << ScanDecoder.FastBits];
public byte this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ref byte self = ref Unsafe.As<FixedByteBuffer512, byte>(ref this);
return Unsafe.Add(ref self, idx);
}
}
}
}

126
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// <summary>
/// Gets the lookahead array
/// </summary>
public FixedInt16Buffer256 Lookahead;
public FixedByteBuffer512 Lookahead;
/// <summary>
/// Gets the sizes array
@ -43,19 +43,76 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// Initializes a new instance of the <see cref="PdfJsHuffmanTable"/> struct.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="lengths">The code lengths</param>
/// <param name="count">The code lengths</param>
/// <param name="values">The huffman values</param>
public PdfJsHuffmanTable(MemoryManager memoryManager, ReadOnlySpan<byte> lengths, ReadOnlySpan<byte> values)
public PdfJsHuffmanTable(MemoryManager memoryManager, ReadOnlySpan<byte> count, ReadOnlySpan<byte> values)
{
const int Length = 257;
using (IBuffer<short> huffcode = memoryManager.Allocate<short>(Length))
{
// Span<short> codes = huffcode.Span;
ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.Span);
this.GenerateSizeTable(lengths);
this.GenerateCodeTable(ref huffcodeRef, Length);
this.GenerateDecoderTables(lengths, ref huffcodeRef);
this.GenerateLookaheadTables(lengths, values, ref huffcodeRef);
this.GenerateSizeTable(count);
//int k = 0;
//fixed (short* sizesRef = this.Sizes.Data)
//fixed (short* deltaRef = this.ValOffset.Data)
//fixed (long* maxcodeRef = this.MaxCode.Data)
//{
// uint code = 0;
// int j;
// for (j = 1; j <= 16; j++)
// {
// // Compute delta to add to code to compute symbol id.
// deltaRef[j] = (short)(k - code);
// if (sizesRef[k] == j)
// {
// while (sizesRef[k] == j)
// {
// codes[k++] = (short)code++;
// // Unsafe.Add(ref huffcodeRef, k++) = (short)code++;
// // TODO: Throw if invalid?
// }
// }
// // Compute largest code + 1 for this size. preshifted as neeed later.
// maxcodeRef[j] = code << (16 - j);
// code <<= 1;
// }
// maxcodeRef[j] = 0xFFFFFFFF;
//}
//fixed (short* lookaheadRef = this.Lookahead.Data)
//{
// const int FastBits = ScanDecoder.FastBits;
// var fast = new Span<short>(lookaheadRef, 1 << FastBits);
// fast.Fill(255); // Flag for non-accelerated
// fixed (short* sizesRef = this.Sizes.Data)
// {
// for (int i = 0; i < k; i++)
// {
// int s = sizesRef[i];
// if (s <= ScanDecoder.FastBits)
// {
// int c = codes[i] << (FastBits - s);
// int m = 1 << (FastBits - s);
// for (int j = 0; j < m; j++)
// {
// fast[c + j] = (byte)i;
// }
// }
// }
// }
//}
this.GenerateCodeTable(ref huffcodeRef, Length, out int k);
this.GenerateDecoderTables(count, ref huffcodeRef);
this.GenerateLookaheadTables(count, values, ref huffcodeRef, k);
}
fixed (byte* huffValRef = this.Values.Data)
@ -74,18 +131,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
fixed (short* sizesRef = this.Sizes.Data)
{
short index = 0;
for (short l = 1; l <= 16; l++)
short k = 0;
for (short i = 1; i < 17; i++)
{
byte i = lengths[l];
for (short j = 0; j < i; j++)
byte l = lengths[i];
for (short j = 0; j < l; j++)
{
sizesRef[index] = l;
index++;
sizesRef[k] = i;
k++;
}
}
sizesRef[index] = 0;
sizesRef[k] = 0;
}
}
@ -94,11 +151,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary>
/// <param name="huffcodeRef">The huffman code span ref</param>
/// <param name="length">The length of the huffsize span</param>
private void GenerateCodeTable(ref short huffcodeRef, int length)
/// <param name="k">The length of any valid codes</param>
private void GenerateCodeTable(ref short huffcodeRef, int length, out int k)
{
fixed (short* sizesRef = this.Sizes.Data)
{
short k = 0;
k = 0;
short si = sizesRef[0];
short code = 0;
for (short i = 0; i < length; i++)
@ -134,7 +192,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
// valOffsetRef[l] = huffcodeRef[] index of 1st symbol of code length i, minus the minimum code of length i
valOffsetRef[i] = (short)(bitcount - Unsafe.Add(ref huffcodeRef, bitcount));
bitcount += lengths[i];
maxcodeRef[i] = Unsafe.Add(ref huffcodeRef, bitcount - 1); // maximum code of length i
maxcodeRef[i] = Unsafe.Add(ref huffcodeRef, bitcount - 1) << (16 - i); // maximum code of length i preshifted for faster reading later
}
else
{
@ -143,41 +201,43 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
valOffsetRef[17] = 0;
maxcodeRef[17] = 0xFFFFFL;
maxcodeRef[17] = 0xFFFFFFFFL;
}
}
/// <summary>
/// Generates lookup tables to speed up decoding
/// Generates non-spec lookup tables to speed up decoding
/// </summary>
/// <param name="lengths">The code lengths</param>
/// <param name="huffval">The huffman value array</param>
/// <param name="huffcodeRef">The huffman code span ref</param>
private void GenerateLookaheadTables(ReadOnlySpan<byte> lengths, ReadOnlySpan<byte> huffval, ref short huffcodeRef)
/// <param name="k">The lengths of any valid codes</param>
private void GenerateLookaheadTables(ReadOnlySpan<byte> lengths, ReadOnlySpan<byte> huffval, ref short huffcodeRef, int k)
{
// TODO: Rewrite this to match stb_Image
// 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)
fixed (byte* lookaheadRef = this.Lookahead.Data)
{
var lookaheadSpan = new Span<short>(lookaheadRef, 256);
lookaheadSpan.Fill(2034); // 9 << 8;
const int FastBits = ScanDecoder.FastBits;
var lookaheadSpan = new Span<short>(lookaheadRef, 1 << ScanDecoder.FastBits);
int p = 0;
for (int l = 1; l <= 8; l++)
lookaheadSpan.Fill(255); // Flag for non-accelerated
fixed (short* sizesRef = this.Sizes.Data)
{
for (int i = 1; i <= lengths[l]; i++, p++)
for (int i = 0; i < k; ++i)
{
// 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--)
int s = sizesRef[i];
if (s <= ScanDecoder.FastBits)
{
lookaheadRef[lookBits] = (short)((l << 8) | huffval[p]);
lookBits++;
int c = Unsafe.Add(ref huffcodeRef, i) << (FastBits - s);
int m = 1 << (FastBits - s);
for (int j = 0; j < m; ++j)
{
lookaheadRef[c + j] = (byte)i;
}
}
}
}

87
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs

@ -15,10 +15,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
public const int FastBits = 9;
// bmask[n] = (1 << n) - 1
private static readonly uint[] stbi__bmask = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535 };
private static readonly uint[] Bmask = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535 };
// bias[n] = (-1 << n) + 1
private static readonly int[] stbi__jbias = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 };
private static readonly int[] Bias = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 };
private readonly DoubleBufferedStreamReader stream;
private readonly PdfJsFrameComponent[] components;
@ -141,8 +141,61 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
else
{
// Interleaved
int i, j, k, x, y;
int mcu = 0;
int mcusPerColumn = frame.McusPerColumn;
int mcusPerLine = frame.McusPerLine;
for (int j = 0; j < mcusPerColumn; j++)
{
for (int i = 0; i < mcusPerLine; i++)
{
// Scan an interleaved mcu... process components in order
for (int k = 0; k < this.componentsLength; k++)
{
PdfJsFrameComponent component = this.components[k];
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
Span<short> fastAC = fastACTables.Tables.GetRowSpan(component.ACHuffmanTableId);
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
// Scan out an mcu's worth of this component; that's just determined
// by the basic H and V specified for the component
for (int y = 0; y < v; y++)
{
for (int x = 0; x < h; x++)
{
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeBlock(component, ref Unsafe.Add(ref blockDataRef, offset), ref dcHuffmanTable, ref acHuffmanTable, fastAC);
}
}
}
// After all interleaved components, that's an interleaved MCU,
// so now count down the restart interval
mcu++;
if (this.todo-- <= 0)
{
if (this.codeBits < 24)
{
this.GrowBufferUnsafe();
}
// If it's NOT a restart, then just bail, so we get corrupt data
// rather than no data
if (!this.IsRestartMarker(this.marker))
{
return 1;
}
this.Reset();
}
}
}
}
}
@ -156,11 +209,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
ref PdfJsHuffmanTable acTable,
Span<short> fac)
{
if (this.codeBits < 16)
{
this.GrowBufferUnsafe();
}
this.CheckBits();
int t = this.DecodeHuffman(ref dcTable);
if (t < 0)
@ -477,8 +526,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
uint k = this.Lrot(this.codeBuffer, n);
this.codeBuffer = k & ~stbi__bmask[n];
k &= stbi__bmask[n];
this.codeBuffer = k & ~Bmask[n];
k &= Bmask[n];
this.codeBits -= n;
return (int)k;
}
@ -503,7 +552,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
do
{
// TODO: EOF
uint b = (uint)(this.nomore ? 0 : this.stream.ReadByte());
int b = this.nomore ? 0 : this.stream.ReadByte();
if (b == PdfJsJpegConstants.Markers.Prefix)
{
long position = this.stream.Position - 1;
@ -514,13 +563,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
this.marker = (byte)c;
this.nomore = true;
this.stream.Position = position;
if (!this.IsRestartMarker(this.marker))
{
this.stream.Position = position;
}
return;
}
}
}
this.codeBuffer |= b << (24 - this.codeBits);
this.codeBuffer |= (uint)b << (24 - this.codeBits);
this.codeBits += 8;
}
while (this.codeBits <= 24);
@ -575,7 +628,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
// Convert the huffman code to the symbol id
c = (int)((this.codeBuffer >> (32 - k)) & stbi__bmask[k]) + table.ValOffset[k];
c = (int)((this.codeBuffer >> (32 - k)) & Bmask[k]) + table.ValOffset[k];
// Convert the id to a symbol
this.codeBits -= k;
@ -593,10 +646,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
int sgn = (int)(this.codeBuffer >> 31);
uint k = this.Lrot(this.codeBuffer, n);
this.codeBuffer = k & ~stbi__bmask[n];
k &= stbi__bmask[n];
this.codeBuffer = k & ~Bmask[n];
k &= Bmask[n];
this.codeBits -= n;
return (int)(k + (stbi__jbias[n] & ~sgn));
return (int)(k + (Bias[n] & ~sgn));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

50
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

@ -731,10 +731,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
codeLengths.Span,
huffmanValues.Span);
if (tableType != 0)
if (huffmanTableSpec >> 4 != 0)
{
// Build a table that decodes both magnitude and value of small ACs in one go.
this.BuildFastACTable(tableIndex);
this.BuildFastACTable(huffmanTableSpec & 15);
}
}
}
@ -794,21 +794,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
int spectralStart = this.temp[0];
int spectralEnd = this.temp[1];
int successiveApproximation = this.temp[2];
var scanDecoder = default(PdfJsScanDecoder);
scanDecoder.DecodeScan(
this.Frame,
this.InputStream,
this.dcHuffmanTables,
this.acHuffmanTables,
this.Frame.Components,
componentIndex,
selectorsCount,
this.resetInterval,
spectralStart,
spectralEnd,
successiveApproximation >> 4,
successiveApproximation & 15);
var sd = new ScanDecoder(
this.InputStream,
this.Frame.Components,
componentIndex,
selectorsCount,
this.resetInterval,
spectralStart,
spectralEnd,
successiveApproximation >> 4,
successiveApproximation & 15);
sd.ParseEntropyCodedData(this.Frame, this.dcHuffmanTables, this.acHuffmanTables, this.fastACTables);
//var scanDecoder = default(PdfJsScanDecoder);
//scanDecoder.DecodeScan(
// this.Frame,
// this.InputStream,
// this.dcHuffmanTables,
// this.acHuffmanTables,
// this.Frame.Components,
// componentIndex,
// selectorsCount,
// this.resetInterval,
// spectralStart,
// spectralEnd,
// successiveApproximation >> 4,
// successiveApproximation & 15);
}
/// <summary>
@ -856,7 +870,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
int i;
for (i = 0; i < (1 << FastBits); i++)
{
short fast = huffman.Lookahead[i];
byte fast = huffman.Lookahead[i];
fastac[i] = 0;
if (fast < 255)
{

Loading…
Cancel
Save