Browse Source

Can now decode that bad progressive image

pull/298/head
James Jackson-South 9 years ago
parent
commit
59c0793b80
  1. 162
      src/ImageSharp/Formats/Jpeg/Port/Components/ScanDecoder.cs
  2. 16
      src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs

162
src/ImageSharp/Formats/Jpeg/Port/Components/ScanDecoder.cs

@ -35,6 +35,8 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
private int successiveACNextValue; private int successiveACNextValue;
private bool endOfStreamReached;
/// <summary> /// <summary>
/// Decodes the spectral scan /// Decodes the spectral scan
/// </summary> /// </summary>
@ -50,8 +52,7 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
/// <param name="spectralEnd">The spectral selection end</param> /// <param name="spectralEnd">The spectral selection end</param>
/// <param name="successivePrev">The successive approximation bit high end</param> /// <param name="successivePrev">The successive approximation bit high end</param>
/// <param name="successive">The successive approximation bit low end</param> /// <param name="successive">The successive approximation bit low end</param>
/// <returns>The <see cref="long"/> representing the processed length in bytes</returns> public void DecodeScan(
public long DecodeScan(
Frame frame, Frame frame,
Stream stream, Stream stream,
HuffmanTables dcHuffmanTables, HuffmanTables dcHuffmanTables,
@ -69,9 +70,10 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
this.specStart = spectralStart; this.specStart = spectralStart;
this.specEnd = spectralEnd; this.specEnd = spectralEnd;
this.successiveState = successive; this.successiveState = successive;
this.endOfStreamReached = false;
bool progressive = frame.Progressive; bool progressive = frame.Progressive;
int mcusPerLine = frame.McusPerLine; int mcusPerLine = frame.McusPerLine;
long startPosition = stream.Position;
int mcu = 0; int mcu = 0;
int mcuExpected; int mcuExpected;
@ -129,7 +131,6 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
// Find marker // Find marker
this.bitsCount = 0; this.bitsCount = 0;
long position = stream.Position;
fileMarker = JpegDecoderCore.FindNextFileMarkerNew(stream); fileMarker = JpegDecoderCore.FindNextFileMarkerNew(stream);
// Some bad images seem to pad Scan blocks with e.g. zero bytes, skip past // Some bad images seem to pad Scan blocks with e.g. zero bytes, skip past
@ -137,20 +138,13 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
if (fileMarker.Invalid) if (fileMarker.Invalid)
{ {
#if DEBUG #if DEBUG
Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker}"); Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}");
#endif #endif
// stream.Position = fileMarker.Position;
} }
ushort marker = fileMarker.Marker; ushort marker = fileMarker.Marker;
// if (marker <= 0xFF00) // RSTn - We've alread read the bytes and altered the position so no need to skip
// {
// throw new ImageFormatException("Marker was not found");
// }
// RSTn We've alread read the bytes and altered the position so no need to skip
if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7) if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7)
{ {
continue; continue;
@ -159,13 +153,10 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
if (!fileMarker.Invalid) if (!fileMarker.Invalid)
{ {
// We've found a valid marker. // We've found a valid marker.
// Rewind the stream to the position of the marker and beak // Rewind the stream to the position of the marker and break
stream.Position = fileMarker.Position; stream.Position = fileMarker.Position;
break; break;
} }
// Rewind the stream
stream.Position = position;
} }
fileMarker = JpegDecoderCore.FindNextFileMarkerNew(stream); fileMarker = JpegDecoderCore.FindNextFileMarkerNew(stream);
@ -175,12 +166,15 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
if (fileMarker.Invalid) if (fileMarker.Invalid)
{ {
#if DEBUG #if DEBUG
Debug.WriteLine($"DecodeScan - Unexpected MCU data, next marker is: {fileMarker.Marker}"); Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}");
#endif #endif
}
else
{
// We've found a valid marker.
// Rewind the stream to the position of the marker
stream.Position = fileMarker.Position; stream.Position = fileMarker.Position;
} }
return stream.Position - startPosition;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -205,6 +199,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
ref FrameComponent component = ref components[this.compIndex]; ref FrameComponent component = ref components[this.compIndex];
for (int n = 0; n < mcuToRead; n++) for (int n = 0; n < mcuToRead; n++)
{ {
if (this.endOfStreamReached)
{
continue;
}
this.DecodeBlockBaseline(dcHuffmanTables, acHuffmanTables, ref component, mcu, stream); this.DecodeBlockBaseline(dcHuffmanTables, acHuffmanTables, ref component, mcu, stream);
mcu++; mcu++;
} }
@ -222,6 +221,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
{ {
for (int k = 0; k < h; k++) for (int k = 0; k < h; k++)
{ {
if (this.endOfStreamReached)
{
continue;
}
this.DecodeMcuBaseline(dcHuffmanTables, acHuffmanTables, ref component, mcusPerLine, mcu, j, k, stream); this.DecodeMcuBaseline(dcHuffmanTables, acHuffmanTables, ref component, mcusPerLine, mcu, j, k, stream);
} }
} }
@ -247,6 +251,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
ref FrameComponent component = ref components[this.compIndex]; ref FrameComponent component = ref components[this.compIndex];
for (int n = 0; n < mcuToRead; n++) for (int n = 0; n < mcuToRead; n++)
{ {
if (this.endOfStreamReached)
{
continue;
}
this.DecodeBlockDCFirst(dcHuffmanTables, ref component, mcu, stream); this.DecodeBlockDCFirst(dcHuffmanTables, ref component, mcu, stream);
mcu++; mcu++;
} }
@ -264,6 +273,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
{ {
for (int k = 0; k < h; k++) for (int k = 0; k < h; k++)
{ {
if (this.endOfStreamReached)
{
continue;
}
this.DecodeMcuDCFirst(dcHuffmanTables, ref component, mcusPerLine, mcu, j, k, stream); this.DecodeMcuDCFirst(dcHuffmanTables, ref component, mcusPerLine, mcu, j, k, stream);
} }
} }
@ -288,6 +302,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
ref FrameComponent component = ref components[this.compIndex]; ref FrameComponent component = ref components[this.compIndex];
for (int n = 0; n < mcuToRead; n++) for (int n = 0; n < mcuToRead; n++)
{ {
if (this.endOfStreamReached)
{
continue;
}
this.DecodeBlockDCSuccessive(ref component, mcu, stream); this.DecodeBlockDCSuccessive(ref component, mcu, stream);
mcu++; mcu++;
} }
@ -305,6 +324,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
{ {
for (int k = 0; k < h; k++) for (int k = 0; k < h; k++)
{ {
if (this.endOfStreamReached)
{
continue;
}
this.DecodeMcuDCSuccessive(ref component, mcusPerLine, mcu, j, k, stream); this.DecodeMcuDCSuccessive(ref component, mcusPerLine, mcu, j, k, stream);
} }
} }
@ -330,6 +354,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
ref FrameComponent component = ref components[this.compIndex]; ref FrameComponent component = ref components[this.compIndex];
for (int n = 0; n < mcuToRead; n++) for (int n = 0; n < mcuToRead; n++)
{ {
if (this.endOfStreamReached)
{
continue;
}
this.DecodeBlockACFirst(acHuffmanTables, ref component, mcu, stream); this.DecodeBlockACFirst(acHuffmanTables, ref component, mcu, stream);
mcu++; mcu++;
} }
@ -347,6 +376,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
{ {
for (int k = 0; k < h; k++) for (int k = 0; k < h; k++)
{ {
if (this.endOfStreamReached)
{
continue;
}
this.DecodeMcuACFirst(acHuffmanTables, ref component, mcusPerLine, mcu, j, k, stream); this.DecodeMcuACFirst(acHuffmanTables, ref component, mcusPerLine, mcu, j, k, stream);
} }
} }
@ -372,6 +406,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
ref FrameComponent component = ref components[this.compIndex]; ref FrameComponent component = ref components[this.compIndex];
for (int n = 0; n < mcuToRead; n++) for (int n = 0; n < mcuToRead; n++)
{ {
if (this.endOfStreamReached)
{
continue;
}
this.DecodeBlockACSuccessive(acHuffmanTables, ref component, mcu, stream); this.DecodeBlockACSuccessive(acHuffmanTables, ref component, mcu, stream);
mcu++; mcu++;
} }
@ -389,6 +428,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
{ {
for (int k = 0; k < h; k++) for (int k = 0; k < h; k++)
{ {
if (this.endOfStreamReached)
{
continue;
}
this.DecodeMcuACSuccessive(acHuffmanTables, ref component, mcusPerLine, mcu, j, k, stream); this.DecodeMcuACSuccessive(acHuffmanTables, ref component, mcusPerLine, mcu, j, k, stream);
} }
} }
@ -509,6 +553,13 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
} }
this.bitsData = stream.ReadByte(); this.bitsData = stream.ReadByte();
if (this.bitsData == -0x1)
{
// We've encountered the end of the file stream which means there's no EOI marker in the image
this.endOfStreamReached = true;
}
if (this.bitsData == 0xFF) if (this.bitsData == 0xFF)
{ {
int nextByte = stream.ReadByte(); int nextByte = stream.ReadByte();
@ -527,10 +578,16 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private short DecodeHuffman(HuffmanBranch[] tree, Stream stream) private short DecodeHuffman(HuffmanBranch[] tree, Stream stream)
{ {
// TODO: This is our bottleneck. We should use a faster algorithm with a LUT.
HuffmanBranch[] node = tree; HuffmanBranch[] node = tree;
while (true) while (true)
{ {
int index = this.ReadBit(stream); int index = this.ReadBit(stream);
if (this.endOfStreamReached)
{
return -1;
}
HuffmanBranch branch = node[index]; HuffmanBranch branch = node[index];
if (branch.Value > -1) if (branch.Value > -1)
@ -548,7 +605,13 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
int n = 0; int n = 0;
while (length > 0) while (length > 0)
{ {
n = (n << 1) | this.ReadBit(stream); int bit = this.ReadBit(stream);
if (this.endOfStreamReached)
{
return -1;
}
n = (n << 1) | bit;
length--; length--;
} }
@ -576,6 +639,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
private void DecodeBaseline(ref FrameComponent component, int offset, HuffmanTables dcHuffmanTables, HuffmanTables acHuffmanTables, Stream stream) private void DecodeBaseline(ref FrameComponent component, int offset, HuffmanTables dcHuffmanTables, HuffmanTables acHuffmanTables, Stream stream)
{ {
int t = this.DecodeHuffman(dcHuffmanTables[component.DCHuffmanTableId], stream); int t = this.DecodeHuffman(dcHuffmanTables[component.DCHuffmanTableId], stream);
if (this.endOfStreamReached)
{
return;
}
int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream); int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream);
component.BlockData[offset] = (short)(component.Pred += diff); component.BlockData[offset] = (short)(component.Pred += diff);
@ -583,6 +651,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
while (k < 64) while (k < 64)
{ {
int rs = this.DecodeHuffman(acHuffmanTables[component.ACHuffmanTableId], stream); int rs = this.DecodeHuffman(acHuffmanTables[component.ACHuffmanTableId], stream);
if (this.endOfStreamReached)
{
return;
}
int s = rs & 15; int s = rs & 15;
int r = rs >> 4; int r = rs >> 4;
@ -609,6 +682,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
private void DecodeDCFirst(ref FrameComponent component, int offset, HuffmanTables dcHuffmanTables, Stream stream) private void DecodeDCFirst(ref FrameComponent component, int offset, HuffmanTables dcHuffmanTables, Stream stream)
{ {
int t = this.DecodeHuffman(dcHuffmanTables[component.DCHuffmanTableId], stream); int t = this.DecodeHuffman(dcHuffmanTables[component.DCHuffmanTableId], stream);
if (this.endOfStreamReached)
{
return;
}
int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream) << this.successiveState; int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream) << this.successiveState;
component.BlockData[offset] = (short)(component.Pred += diff); component.BlockData[offset] = (short)(component.Pred += diff);
} }
@ -616,7 +694,13 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeDCSuccessive(ref FrameComponent component, int offset, Stream stream) private void DecodeDCSuccessive(ref FrameComponent component, int offset, Stream stream)
{ {
component.BlockData[offset] |= (short)(this.ReadBit(stream) << this.successiveState); int bit = this.ReadBit(stream);
if (this.endOfStreamReached)
{
return;
}
component.BlockData[offset] |= (short)(bit << this.successiveState);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -633,6 +717,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
while (k <= e) while (k <= e)
{ {
short rs = this.DecodeHuffman(acHuffmanTables[component.ACHuffmanTableId], stream); short rs = this.DecodeHuffman(acHuffmanTables[component.ACHuffmanTableId], stream);
if (this.endOfStreamReached)
{
return;
}
int s = rs & 15; int s = rs & 15;
int r = rs >> 4; int r = rs >> 4;
@ -668,6 +757,11 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
{ {
case 0: // Initial state case 0: // Initial state
short rs = this.DecodeHuffman(acHuffmanTables[component.ACHuffmanTableId], stream); short rs = this.DecodeHuffman(acHuffmanTables[component.ACHuffmanTableId], stream);
if (this.endOfStreamReached)
{
return;
}
int s = rs & 15; int s = rs & 15;
r = rs >> 4; r = rs >> 4;
if (s == 0) if (s == 0)
@ -699,7 +793,13 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
case 2: case 2:
if (component.BlockData[offset + z] != 0) if (component.BlockData[offset + z] != 0)
{ {
component.BlockData[offset + z] += (short)(this.ReadBit(stream) << this.successiveState); int bit = this.ReadBit(stream);
if (this.endOfStreamReached)
{
return;
}
component.BlockData[offset + z] += (short)(bit << this.successiveState);
} }
else else
{ {
@ -714,7 +814,13 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
case 3: // Set value for a zero item case 3: // Set value for a zero item
if (component.BlockData[offset + z] != 0) if (component.BlockData[offset + z] != 0)
{ {
component.BlockData[offset + z] += (short)(this.ReadBit(stream) << this.successiveState); int bit = this.ReadBit(stream);
if (this.endOfStreamReached)
{
return;
}
component.BlockData[offset + z] += (short)(bit << this.successiveState);
} }
else else
{ {
@ -726,7 +832,13 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
case 4: // Eob case 4: // Eob
if (component.BlockData[offset + z] != 0) if (component.BlockData[offset + z] != 0)
{ {
component.BlockData[offset + z] += (short)(this.ReadBit(stream) << this.successiveState); int bit = this.ReadBit(stream);
if (this.endOfStreamReached)
{
return;
}
component.BlockData[offset + z] += (short)(bit << this.successiveState);
} }
break; break;

16
src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs

@ -720,7 +720,7 @@ namespace ImageSharp.Formats.Jpeg.Port
int successiveApproximation = this.temp[2]; int successiveApproximation = this.temp[2];
var scanDecoder = default(ScanDecoder); var scanDecoder = default(ScanDecoder);
long position = scanDecoder.DecodeScan( scanDecoder.DecodeScan(
this.frame, this.frame,
this.InputStream, this.InputStream,
this.dcHuffmanTables, this.dcHuffmanTables,
@ -733,20 +733,6 @@ namespace ImageSharp.Formats.Jpeg.Port
spectralEnd, spectralEnd,
successiveApproximation >> 4, successiveApproximation >> 4,
successiveApproximation & 15); successiveApproximation & 15);
this.InputStream.Position += position;
Debug.WriteLine("spectralStart= " + spectralStart);
Debug.WriteLine("spectralEnd= " + spectralEnd);
Debug.WriteLine("successiveApproximation= " + successiveApproximation);
Debug.WriteLine("Components after");
//for (int i = 0; i < 3; i++)
//{
// for (int j = 0; j < 10; j++)
// {
// Debug.WriteLine("component [" + i + "] : value [" + j + "] =" + this.frame.Components[i].BlockData[j] + "]");
// }
//}
} }
/// <summary> /// <summary>

Loading…
Cancel
Save