Browse Source

Fix progressive decoding

pull/298/head
James Jackson-South 9 years ago
parent
commit
4a4e94d74a
  1. 98
      src/ImageSharp/Formats/Jpeg/Port/Components/ScanDecoder.cs
  2. 50
      src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs

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

@ -49,18 +49,31 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
/// <param name="dcHuffmanTables">The DC Huffman tables</param> /// <param name="dcHuffmanTables">The DC Huffman tables</param>
/// <param name="acHuffmanTables">The AC Huffman tables</param> /// <param name="acHuffmanTables">The AC Huffman tables</param>
/// <param name="components">The scan components</param> /// <param name="components">The scan components</param>
/// <param name="componentIndex">The component index within the array</param>
/// <param name="componentsLength">The length of the components. Different to the array length</param>
/// <param name="resetInterval">The reset interval</param> /// <param name="resetInterval">The reset interval</param>
/// <param name="spectralStart">The spectral selection start</param> /// <param name="spectralStart">The spectral selection start</param>
/// <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>
public void DecodeScan(Frame frame, Stream stream, HuffmanTables dcHuffmanTables, HuffmanTables acHuffmanTables, FrameComponent[] components, ushort resetInterval, int spectralStart, int spectralEnd, int successivePrev, int successive) public void DecodeScan(
Frame frame,
Stream stream,
HuffmanTables dcHuffmanTables,
HuffmanTables acHuffmanTables,
FrameComponent[] components,
int componentIndex,
int componentsLength,
ushort resetInterval,
int spectralStart,
int spectralEnd,
int successivePrev,
int successive)
{ {
this.specStart = spectralStart; this.specStart = spectralStart;
this.specEnd = spectralEnd; this.specEnd = spectralEnd;
this.successiveState = successive; this.successiveState = successive;
bool progressive = frame.Progressive; bool progressive = frame.Progressive;
int componentsLength = components.Length;
int mcusPerLine = frame.McusPerLine; int mcusPerLine = frame.McusPerLine;
// TODO: Delegate action will not be fast // TODO: Delegate action will not be fast
@ -100,14 +113,14 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
int mcuExpected; int mcuExpected;
if (componentsLength == 1) if (componentsLength == 1)
{ {
mcuExpected = components[0].BlocksPerLine * components[0].BlocksPerColumn; mcuExpected = components[componentIndex].BlocksPerLine * components[componentIndex].BlocksPerColumn;
} }
else else
{ {
mcuExpected = mcusPerLine * frame.McusPerColumn; mcuExpected = mcusPerLine * frame.McusPerColumn;
} }
FileMarker fileMarker; // FileMarker fileMarker;
while (mcu < mcuExpected) while (mcu < mcuExpected)
{ {
// Reset interval stuff // Reset interval stuff
@ -122,7 +135,7 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
if (componentsLength == 1) if (componentsLength == 1)
{ {
ref FrameComponent component = ref components[0]; ref FrameComponent component = ref components[componentIndex];
for (int n = 0; n < mcuToRead; n++) for (int n = 0; n < mcuToRead; n++)
{ {
DecodeBlock(dcHuffmanTables, acHuffmanTables, ref component, decodeFn, mcu, stream); DecodeBlock(dcHuffmanTables, acHuffmanTables, ref component, decodeFn, mcu, stream);
@ -154,45 +167,41 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
// Find marker // Find marker
this.bitsCount = 0; this.bitsCount = 0;
// TODO: We need to make sure we are not overwriting anything here. // // TODO: We need to make sure we are not overwriting anything here.
fileMarker = JpegDecoderCore.FindNextFileMarker(stream); // fileMarker = JpegDecoderCore.FindNextFileMarker(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 // // those to attempt to find a valid marker (fixes issue4090.pdf) in original code.
// those to attempt to find a valid marker (fixes issue4090.pdf) in original code. // if (fileMarker.Invalid)
if (fileMarker.Invalid) // {
{ // #if DEBUG
#if DEBUG // Debug.WriteLine("DecodeScan - Unexpected MCU data, next marker is: " + fileMarker.Marker.ToString("X"));
Debug.WriteLine("DecodeScan - Unexpected MCU data, next marker is: " + fileMarker.Marker.ToString("X")); // #endif
#endif // }
} // ushort marker = fileMarker.Marker;
// if (marker <= 0xFF00)
ushort marker = fileMarker.Marker; // {
if (marker <= 0xFF00) // throw new ImageFormatException("Marker was not found");
{ // }
throw new ImageFormatException("Marker was not found"); // if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7)
} // {
// // RSTx
if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7) // stream.Skip(2);
{ // }
// RSTx // else
stream.Skip(2); // {
} // break;
else // }
{
break;
}
} }
fileMarker = JpegDecoderCore.FindNextFileMarker(stream); // fileMarker = JpegDecoderCore.FindNextFileMarker(stream);
// // Some images include more Scan blocks than expected, skip past those and
// Some images include more Scan blocks than expected, skip past those and // // attempt to find the next valid marker (fixes issue8182.pdf) in original code.
// attempt to find the next valid marker (fixes issue8182.pdf) in original code. // if (fileMarker.Invalid)
if (fileMarker.Invalid) // {
{ // #if DEBUG
#if DEBUG // Debug.WriteLine("DecodeScan - Unexpected MCU data, next marker is: " + fileMarker.Marker.ToString("X"));
Debug.WriteLine("DecodeScan - Unexpected MCU data, next marker is: " + fileMarker.Marker.ToString("X")); // #endif
#endif // }
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -231,7 +240,7 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
} }
this.bitsData = stream.ReadByte(); this.bitsData = stream.ReadByte();
if (this.bitsData == 0xFF) if (this.bitsData == JpegConstants.Markers.Prefix)
{ {
int nextByte = stream.ReadByte(); int nextByte = stream.ReadByte();
if (nextByte > 0) if (nextByte > 0)
@ -252,8 +261,7 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
HuffmanBranch[] node = tree; HuffmanBranch[] node = tree;
while (true) while (true)
{ {
int index; int index = this.ReadBit(stream);
index = this.ReadBit(stream);
HuffmanBranch branch = node[index]; HuffmanBranch branch = node[index];
node = branch.Children; node = branch.Children;

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

@ -87,35 +87,33 @@ namespace ImageSharp.Formats.Jpeg.Port
/// </summary> /// </summary>
/// <param name="stream">The input stream</param> /// <param name="stream">The input stream</param>
/// <returns>The <see cref="FileMarker"/></returns> /// <returns>The <see cref="FileMarker"/></returns>
public static FileMarker FindNextFileMarkerOld(Stream stream) public static FileMarker FindNextFileMarkerNew(Stream stream)
{ {
byte[] buffer = new byte[2]; byte[] marker = new byte[2];
int value = stream.Read(buffer, 0, 2); int value = stream.Read(marker, 0, 2);
if (value == 0) if (value == 0)
{ {
return new FileMarker(JpegConstants.Markers.EOI, (int)stream.Length, true); return new FileMarker(JpegConstants.Markers.EOI, (int)stream.Length, true);
} }
// According to Section B.1.1.2: if (marker[0] == JpegConstants.Markers.Prefix)
// "Any marker may optionally be preceded by any number of fill bytes, which are bytes assigned code 0xFF."
if (buffer[1] != JpegConstants.Markers.Prefix)
{
return new FileMarker((ushort)((buffer[0] << 8) | buffer[1]), (int)(stream.Position - 2));
}
while (buffer[1] == JpegConstants.Markers.Prefix)
{ {
int suffix = stream.ReadByte(); // According to Section B.1.1.2:
if (suffix == -1) // "Any marker may optionally be preceded by any number of fill bytes, which are bytes assigned code 0xFF."
while (marker[1] == JpegConstants.Markers.Prefix)
{ {
return new FileMarker(JpegConstants.Markers.EOI, (int)stream.Length, true); int suffix = stream.ReadByte();
} if (suffix == -1)
{
return new FileMarker(JpegConstants.Markers.EOI, (int)stream.Length, true);
}
buffer[1] = (byte)value; marker[1] = (byte)value;
}
} }
return new FileMarker((ushort)((buffer[0] << 8) | buffer[1]), (int)(stream.Position - 2)); return new FileMarker((ushort)((marker[0] << 8) | marker[1]), (int)(stream.Position - 2));
} }
/// <summary> /// <summary>
@ -292,6 +290,7 @@ namespace ImageSharp.Formats.Jpeg.Port
default: default:
// TODO: Not convinced this is required
// Skip back as it could be incorrect encoding -- last 0xFF byte of the previous // Skip back as it could be incorrect encoding -- last 0xFF byte of the previous
// block was eaten by the encoder // block was eaten by the encoder
this.InputStream.Position -= 3; this.InputStream.Position -= 3;
@ -308,7 +307,7 @@ namespace ImageSharp.Formats.Jpeg.Port
} }
// Read on. TODO: Test this on damaged images. // Read on. TODO: Test this on damaged images.
fileMarker = FindNextFileMarkerOld(this.InputStream); fileMarker = FindNextFileMarkerNew(this.InputStream);
} }
this.width = this.frame.SamplesPerLine; this.width = this.frame.SamplesPerLine;
@ -547,7 +546,7 @@ namespace ImageSharp.Formats.Jpeg.Port
component.VerticalFactor = v; component.VerticalFactor = v;
component.QuantizationIdentifier = this.temp[index + 2]; component.QuantizationIdentifier = this.temp[index + 2];
this.frame.ComponentIds[i] = (byte)i; this.frame.ComponentIds[i] = component.Id;
index += 3; index += 3;
} }
@ -623,16 +622,18 @@ namespace ImageSharp.Formats.Jpeg.Port
private void ProcessStartOfScanMarker() private void ProcessStartOfScanMarker()
{ {
int selectorsCount = this.InputStream.ReadByte(); int selectorsCount = this.InputStream.ReadByte();
int index = -1;
for (int i = 0; i < selectorsCount; i++) for (int i = 0; i < selectorsCount; i++)
{ {
int index = -1; index = -1;
int selector = this.InputStream.ReadByte(); int selector = this.InputStream.ReadByte();
foreach (byte id in this.frame.ComponentIds) for (int j = 0; j < this.frame.ComponentIds.Length; j++)
{ {
byte id = this.frame.ComponentIds[j];
if (selector == id) if (selector == id)
{ {
index = selector; index = j;
} }
} }
@ -641,8 +642,7 @@ namespace ImageSharp.Formats.Jpeg.Port
throw new ImageFormatException("Unknown component selector"); throw new ImageFormatException("Unknown component selector");
} }
byte componentIndex = this.frame.ComponentIds[index]; ref FrameComponent component = ref this.frame.Components[index];
ref FrameComponent component = ref this.frame.Components[componentIndex];
int tableSpec = this.InputStream.ReadByte(); int tableSpec = this.InputStream.ReadByte();
component.DCHuffmanTableId = tableSpec >> 4; component.DCHuffmanTableId = tableSpec >> 4;
component.ACHuffmanTableId = tableSpec & 15; component.ACHuffmanTableId = tableSpec & 15;
@ -661,6 +661,8 @@ namespace ImageSharp.Formats.Jpeg.Port
this.dcHuffmanTables, this.dcHuffmanTables,
this.acHuffmanTables, this.acHuffmanTables,
this.frame.Components, this.frame.Components,
index,
selectorsCount,
this.resetInterval, this.resetInterval,
spectralStart, spectralStart,
spectralEnd, spectralEnd,

Loading…
Cancel
Save