Browse Source

Can now decode baseline + progressive

af/merge-core
James Jackson-South 8 years ago
parent
commit
4fddef4e1d
  1. 207
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs

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

@ -25,7 +25,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
private int codeBits; private int codeBits;
private uint codeBuffer; private uint codeBuffer;
private bool nomore; private bool nomore;
private bool eof;
private byte marker; private byte marker;
private bool badMarker;
private long markerPosition;
private int todo; private int todo;
private int restartInterval; private int restartInterval;
@ -64,6 +67,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.stream = stream; this.stream = stream;
this.components = components; this.components = components;
this.marker = JpegConstants.Markers.XFF; this.marker = JpegConstants.Markers.XFF;
this.markerPosition = 0;
this.componentIndex = componentIndex; this.componentIndex = componentIndex;
this.componentsLength = componentsLength; this.componentsLength = componentsLength;
this.restartInterval = restartInterval; this.restartInterval = restartInterval;
@ -104,13 +108,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span)); ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
Span<short> fastAC = fastACTables.Tables.GetRowSpan(component.ACHuffmanTableId); ReadOnlySpan<short> fastAC = fastACTables.Tables.GetRowSpan(component.ACHuffmanTableId);
int mcu = 0; int mcu = 0;
for (int j = 0; j < h; j++) for (int j = 0; j < h; j++)
{ {
for (int i = 0; i < w; i++) for (int i = 0; i < w; i++)
{ {
if (this.eof)
{
continue;
}
int blockRow = mcu / w; int blockRow = mcu / w;
int blockCol = mcu % w; int blockCol = mcu % w;
int offset = component.GetBlockBufferOffset(blockRow, blockCol); int offset = component.GetBlockBufferOffset(blockRow, blockCol);
@ -154,7 +163,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span)); ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
Span<short> fastAC = fastACTables.Tables.GetRowSpan(component.ACHuffmanTableId); ReadOnlySpan<short> fastAC = fastACTables.Tables.GetRowSpan(component.ACHuffmanTableId);
int h = component.HorizontalSamplingFactor; int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor; int v = component.VerticalSamplingFactor;
@ -164,6 +173,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{ {
for (int x = 0; x < h; x++) for (int x = 0; x < h; x++)
{ {
if (this.eof)
{
continue;
}
int mcuRow = mcu / mcusPerLine; int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine; int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * v) + y; int blockRow = (mcuRow * v) + y;
@ -197,6 +211,138 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
} }
} }
} }
else
{
if (this.componentsLength == 1)
{
PdfJsFrameComponent component = this.components[this.componentIndex];
// Non-interleaved data, we just need to process one block at a time,
// in trivial scanline order
// number of blocks to do just depends on how many actual "pixels" this
// component has, independent of interleaved MCU blocking and such
int w = component.WidthInBlocks;
int h = component.HeightInBlocks;
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];
ReadOnlySpan<short> fastAC = fastACTables.Tables.GetRowSpan(component.ACHuffmanTableId);
int mcu = 0;
for (int j = 0; j < h; j++)
{
for (int i = 0; i < w; i++)
{
if (this.eof)
{
continue;
}
int blockRow = mcu / w;
int blockCol = mcu % w;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
if (this.spectralStart == 0)
{
this.DecodeBlockProgressiveDC(component, ref Unsafe.Add(ref blockDataRef, offset), ref dcHuffmanTable);
}
else
{
this.DecodeBlockProgressiveAC(ref Unsafe.Add(ref blockDataRef, offset), ref acHuffmanTable, fastAC);
}
mcu++;
// Every data block is an MCU, so countdown the restart interval
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();
}
}
}
}
else
{
// Interleaved
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];
ReadOnlySpan<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++)
{
if (this.eof)
{
continue;
}
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.DecodeBlockProgressiveDC(component, ref Unsafe.Add(ref blockDataRef, offset), ref dcHuffmanTable);
}
}
}
// 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();
}
}
}
}
}
if (this.badMarker)
{
this.stream.Position = this.markerPosition;
}
return 1; return 1;
} }
@ -206,7 +352,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
ref short blockDataRef, ref short blockDataRef,
ref PdfJsHuffmanTable dcTable, ref PdfJsHuffmanTable dcTable,
ref PdfJsHuffmanTable acTable, ref PdfJsHuffmanTable acTable,
Span<short> fastAc) ReadOnlySpan<short> fastAc)
{ {
this.CheckBits(); this.CheckBits();
int t = this.DecodeHuffman(ref dcTable); int t = this.DecodeHuffman(ref dcTable);
@ -295,7 +441,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{ {
// First scan for DC coefficient, must be first // First scan for DC coefficient, must be first
int t = this.DecodeHuffman(ref dcTable); int t = this.DecodeHuffman(ref dcTable);
int diff = t > 0 ? this.ExtendReceive(t) : 0; int diff = t != 0 ? this.ExtendReceive(t) : 0;
int dc = component.DcPredictor + diff; int dc = component.DcPredictor + diff;
component.DcPredictor = dc; component.DcPredictor = dc;
@ -305,7 +451,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
else else
{ {
// Refinement scan for DC coefficient // Refinement scan for DC coefficient
if (this.GetBit() > 0) if (this.GetBit() != 0)
{ {
blockDataRef += (short)(1 << this.successiveLow); blockDataRef += (short)(1 << this.successiveLow);
} }
@ -315,10 +461,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
} }
private int DecodeBlockProgressiveAC( private int DecodeBlockProgressiveAC(
PdfJsFrameComponent component,
ref short blockDataRef, ref short blockDataRef,
ref PdfJsHuffmanTable acTable, ref PdfJsHuffmanTable acTable,
Span<short> fac) ReadOnlySpan<short> fastAc)
{ {
int k; int k;
@ -331,7 +476,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{ {
int shift = this.successiveLow; int shift = this.successiveLow;
if (this.eobrun > 0) if (this.eobrun != 0)
{ {
this.eobrun--; this.eobrun--;
return 1; return 1;
@ -345,9 +490,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.CheckBits(); this.CheckBits();
int c = this.PeekBits(); int c = this.PeekBits();
int r = fac[c]; int r = fastAc[c];
if (r > 0) if (r != 0)
{ {
// Fast AC path // Fast AC path
k += (r >> 4) & 15; // Run k += (r >> 4) & 15; // Run
@ -376,7 +521,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
if (r < 15) if (r < 15)
{ {
this.eobrun = 1 << r; this.eobrun = 1 << r;
if (r > 0) if (r != 0)
{ {
this.eobrun += this.GetBits(r); this.eobrun += this.GetBits(r);
} }
@ -402,15 +547,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
// Refinement scan for these AC coefficients // Refinement scan for these AC coefficients
short bit = (short)(1 << this.successiveLow); short bit = (short)(1 << this.successiveLow);
if (this.eobrun > 0) if (this.eobrun != 0)
{ {
this.eobrun--; this.eobrun--;
for (k = this.spectralStart; k < this.spectralEnd; k++) for (k = this.spectralStart; k <= this.spectralEnd; k++)
{ {
ref short p = ref Unsafe.Add(ref blockDataRef, this.dctZigZag[k]); ref short p = ref Unsafe.Add(ref blockDataRef, this.dctZigZag[k]);
if (p != 0) if (p != 0)
{ {
if (this.GetBit() > 0) if (this.GetBit() != 0)
{ {
if ((p & bit) == 0) if ((p & bit) == 0)
{ {
@ -450,7 +595,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{ {
this.eobrun = (1 << r) - 1; this.eobrun = (1 << r) - 1;
if (r > 0) if (r != 0)
{ {
this.eobrun += this.GetBits(r); this.eobrun += this.GetBits(r);
} }
@ -466,13 +611,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
} }
// Sign bit // Sign bit
if (this.GetBit() > 0) if (this.GetBit() != 0)
{ {
s = bit; s = bit;
} }
else else
{ {
s -= bit; s = -bit;
} }
} }
@ -482,7 +627,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
ref short p = ref Unsafe.Add(ref blockDataRef, this.dctZigZag[k++]); ref short p = ref Unsafe.Add(ref blockDataRef, this.dctZigZag[k++]);
if (p != 0) if (p != 0)
{ {
if (this.GetBit() > 0) if (this.GetBit() != 0)
{ {
if ((p & bit) == 0) if ((p & bit) == 0)
{ {
@ -553,18 +698,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{ {
// TODO: EOF // TODO: EOF
int b = this.nomore ? 0 : this.stream.ReadByte(); int b = this.nomore ? 0 : this.stream.ReadByte();
if (b == -1)
{
this.eof = true;
b = 0;
}
if (b == JpegConstants.Markers.XFF) if (b == JpegConstants.Markers.XFF)
{ {
this.markerPosition = this.stream.Position - 1;
int c = this.stream.ReadByte(); int c = this.stream.ReadByte();
while (c == JpegConstants.Markers.XFF) while (c == JpegConstants.Markers.XFF)
{ {
c = this.stream.ReadByte(); c = this.stream.ReadByte();
if (c == -1)
{
this.eof = true;
c = 0;
break;
}
} }
if (c != 0) if (c != 0)
{ {
this.marker = (byte)c; this.marker = (byte)c;
this.nomore = true; this.nomore = true;
if (!this.IsRestartMarker(this.marker))
{
this.badMarker = true;
}
return; return;
} }
} }
@ -688,7 +853,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{ {
this.codeBits = 0; this.codeBits = 0;
this.codeBuffer = 0; this.codeBuffer = 0;
this.nomore = false;
for (int i = 0; i < this.components.Length; i++) for (int i = 0; i < this.components.Length; i++)
{ {
@ -696,11 +860,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
c.DcPredictor = 0; c.DcPredictor = 0;
} }
this.nomore = false;
this.marker = JpegConstants.Markers.XFF; this.marker = JpegConstants.Markers.XFF;
this.markerPosition = 0;
this.badMarker = false;
this.eobrun = 0; this.eobrun = 0;
// No more than 1<<31 MCUs if no restartInterval? that's plenty safe since we don't even allow 1<<30 pixels // No more than 1<<31 MCUs if no restartInterval? that's plenty safe since we don't even allow 1<<30 pixels
this.todo = this.restartInterval > 0 ? this.restartInterval : 0x7FFFFFFF; this.todo = this.restartInterval > 0 ? this.restartInterval : int.MaxValue;
} }
} }
} }
Loading…
Cancel
Save