Browse Source

Fix progressive decoding

af/merge-core
James Jackson-South 9 years ago
parent
commit
33893e26e9
  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="acHuffmanTables">The AC Huffman tables</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="spectralStart">The spectral selection start</param>
/// <param name="spectralEnd">The spectral selection end</param>
/// <param name="successivePrev">The successive approximation bit high 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.specEnd = spectralEnd;
this.successiveState = successive;
bool progressive = frame.Progressive;
int componentsLength = components.Length;
int mcusPerLine = frame.McusPerLine;
// TODO: Delegate action will not be fast
@ -100,14 +113,14 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
int mcuExpected;
if (componentsLength == 1)
{
mcuExpected = components[0].BlocksPerLine * components[0].BlocksPerColumn;
mcuExpected = components[componentIndex].BlocksPerLine * components[componentIndex].BlocksPerColumn;
}
else
{
mcuExpected = mcusPerLine * frame.McusPerColumn;
}
FileMarker fileMarker;
// FileMarker fileMarker;
while (mcu < mcuExpected)
{
// Reset interval stuff
@ -122,7 +135,7 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
if (componentsLength == 1)
{
ref FrameComponent component = ref components[0];
ref FrameComponent component = ref components[componentIndex];
for (int n = 0; n < mcuToRead; n++)
{
DecodeBlock(dcHuffmanTables, acHuffmanTables, ref component, decodeFn, mcu, stream);
@ -154,45 +167,41 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
// Find marker
this.bitsCount = 0;
// TODO: We need to make sure we are not overwriting anything here.
fileMarker = JpegDecoderCore.FindNextFileMarker(stream);
// 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.
if (fileMarker.Invalid)
{
#if DEBUG
Debug.WriteLine("DecodeScan - Unexpected MCU data, next marker is: " + fileMarker.Marker.ToString("X"));
#endif
}
ushort marker = fileMarker.Marker;
if (marker <= 0xFF00)
{
throw new ImageFormatException("Marker was not found");
}
if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7)
{
// RSTx
stream.Skip(2);
}
else
{
break;
}
// // TODO: We need to make sure we are not overwriting anything here.
// fileMarker = JpegDecoderCore.FindNextFileMarker(stream);
// // 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.
// if (fileMarker.Invalid)
// {
// #if DEBUG
// Debug.WriteLine("DecodeScan - Unexpected MCU data, next marker is: " + fileMarker.Marker.ToString("X"));
// #endif
// }
// ushort marker = fileMarker.Marker;
// if (marker <= 0xFF00)
// {
// throw new ImageFormatException("Marker was not found");
// }
// if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7)
// {
// // RSTx
// stream.Skip(2);
// }
// else
// {
// break;
// }
}
fileMarker = JpegDecoderCore.FindNextFileMarker(stream);
// 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.
if (fileMarker.Invalid)
{
#if DEBUG
Debug.WriteLine("DecodeScan - Unexpected MCU data, next marker is: " + fileMarker.Marker.ToString("X"));
#endif
}
// fileMarker = JpegDecoderCore.FindNextFileMarker(stream);
// // 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.
// if (fileMarker.Invalid)
// {
// #if DEBUG
// Debug.WriteLine("DecodeScan - Unexpected MCU data, next marker is: " + fileMarker.Marker.ToString("X"));
// #endif
// }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -231,7 +240,7 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
}
this.bitsData = stream.ReadByte();
if (this.bitsData == 0xFF)
if (this.bitsData == JpegConstants.Markers.Prefix)
{
int nextByte = stream.ReadByte();
if (nextByte > 0)
@ -252,8 +261,7 @@ namespace ImageSharp.Formats.Jpeg.Port.Components
HuffmanBranch[] node = tree;
while (true)
{
int index;
index = this.ReadBit(stream);
int index = this.ReadBit(stream);
HuffmanBranch branch = node[index];
node = branch.Children;

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

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

Loading…
Cancel
Save