From 33893e26e9c2581b96e4cc5faafa0560c70d8b2e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 26 Jun 2017 01:18:31 +1000 Subject: [PATCH] Fix progressive decoding --- .../Jpeg/Port/Components/ScanDecoder.cs | 98 ++++++++++--------- .../Formats/Jpeg/Port/JpegDecoderCore.cs | 50 +++++----- 2 files changed, 79 insertions(+), 69 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Port/Components/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Port/Components/ScanDecoder.cs index 09837aef0..b0c9979d2 100644 --- a/src/ImageSharp/Formats/Jpeg/Port/Components/ScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Port/Components/ScanDecoder.cs @@ -49,18 +49,31 @@ namespace ImageSharp.Formats.Jpeg.Port.Components /// The DC Huffman tables /// The AC Huffman tables /// The scan components + /// The component index within the array + /// The length of the components. Different to the array length /// The reset interval /// The spectral selection start /// The spectral selection end /// The successive approximation bit high end /// The successive approximation bit low end - 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; diff --git a/src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs index 5d50ae535..abdabcd49 100644 --- a/src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs @@ -87,35 +87,33 @@ namespace ImageSharp.Formats.Jpeg.Port /// /// The input stream /// The - 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)); } /// @@ -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,