diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs
index fd6a7833a7..cb4b63cffd 100644
--- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs
@@ -380,5 +380,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.LastErrorCode = this.Bits.ReceiveExtendUnsafe(t, ref this, out x);
return this.LastErrorCode;
}
+
+ ///
+ /// Reset the Huffman decoder.
+ ///
+ public void ResetHuffmanDecoder()
+ {
+ this.Bits = default(Bits);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs
index 2e1372c564..d10def3ce7 100644
--- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs
@@ -94,6 +94,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
///
private int eobRun;
+ ///
+ /// The block counter
+ ///
+ private int blockCounter;
+
+ ///
+ /// The MCU counter
+ ///
+ private int mcuCounter;
+
+ ///
+ /// The expected RST marker value
+ ///
+ private byte expectedRst;
+
///
/// Initializes a default-constructed instance for reading data from -s stream.
///
@@ -139,124 +154,136 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{
decoder.InputProcessor.ResetErrorState();
- int blockCount = 0;
- int mcu = 0;
- byte expectedRst = OrigJpegConstants.Markers.RST0;
+ this.blockCounter = 0;
+ this.mcuCounter = 0;
+ this.expectedRst = OrigJpegConstants.Markers.RST0;
for (int my = 0; my < decoder.MCUCountY; my++)
{
for (int mx = 0; mx < decoder.MCUCountX; mx++)
{
- for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++)
- {
- this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex;
- OrigComponent component = decoder.Components[this.ComponentIndex];
+ this.DecodeBlocksAtMcuIndex(decoder, mx, my);
- this.hi = component.HorizontalSamplingFactor;
- int vi = component.VerticalSamplingFactor;
-
- for (int j = 0; j < this.hi * vi; j++)
- {
- if (this.componentScanCount != 1)
- {
- this.bx = (this.hi * mx) + (j % this.hi);
- this.by = (vi * my) + (j / this.hi);
- }
- else
- {
- int q = decoder.MCUCountX * this.hi;
- this.bx = blockCount % q;
- this.by = blockCount / q;
- blockCount++;
- if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight)
- {
- continue;
- }
- }
+ this.mcuCounter++;
- // Find the block at (bx,by) in the component's buffer:
- ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by);
+ // Handling restart intervals
+ // Useful info: https://stackoverflow.com/a/8751802
+ if (decoder.IsAtRestartInterval(this.mcuCounter))
+ {
+ this.ProcessRSTMarker(decoder);
+ this.Reset(decoder);
+ }
+ }
+ }
+ }
- // Copy block to stack
- this.data.Block = blockRefOnHeap;
+ private void DecodeBlocksAtMcuIndex(OrigJpegDecoderCore decoder, int mx, int my)
+ {
+ for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++)
+ {
+ this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex;
+ OrigComponent component = decoder.Components[this.ComponentIndex];
- if (!decoder.InputProcessor.ReachedEOF)
- {
- this.DecodeBlock(decoder, scanIndex);
- }
+ this.hi = component.HorizontalSamplingFactor;
+ int vi = component.VerticalSamplingFactor;
- // Store the result block:
- blockRefOnHeap = this.data.Block;
+ for (int j = 0; j < this.hi * vi; j++)
+ {
+ if (this.componentScanCount != 1)
+ {
+ this.bx = (this.hi * mx) + (j % this.hi);
+ this.by = (vi * my) + (j / this.hi);
+ }
+ else
+ {
+ int q = decoder.MCUCountX * this.hi;
+ this.bx = this.blockCounter % q;
+ this.by = this.blockCounter / q;
+ this.blockCounter++;
+ if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight)
+ {
+ continue;
}
+ }
+
+ // Find the block at (bx,by) in the component's buffer:
+ ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by);
- // for j
+ // Copy block to stack
+ this.data.Block = blockRefOnHeap;
+
+ if (!decoder.InputProcessor.ReachedEOF)
+ {
+ this.DecodeBlock(decoder, scanIndex);
}
- // for i
- mcu++;
+ // Store the result block:
+ blockRefOnHeap = this.data.Block;
+ }
+ }
+ }
- if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < decoder.TotalMCUCount)
+ private void ProcessRSTMarker(OrigJpegDecoderCore decoder)
+ {
+ // Attempt to look for RST[0-7] markers to resynchronize from corrupt input.
+ if (!decoder.InputProcessor.ReachedEOF)
+ {
+ decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2);
+ if (decoder.InputProcessor.CheckEOFEnsureNoError())
+ {
+ if (decoder.Temp[0] != 0xFF || decoder.Temp[1] != this.expectedRst)
{
- // Attempt to look for RST[0-7] markers to resynchronize from corrupt input.
- if (!decoder.InputProcessor.ReachedEOF)
+ bool invalidRst = true;
+
+ // Most jpeg's containing well-formed input will have a RST[0-7] marker following immediately
+ // but some, see Issue #481, contain padding bytes "0xFF" before the RST[0-7] marker.
+ // If we identify that case we attempt to read until we have bypassed the padded bytes.
+ // We then check again for our RST marker and throw if invalid.
+ // No other methods are attempted to resynchronize from corrupt input.
+ if (decoder.Temp[0] == 0xFF && decoder.Temp[1] == 0xFF)
{
- decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2);
- if (decoder.InputProcessor.CheckEOFEnsureNoError())
+ while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError())
{
- if (decoder.Temp[0] != 0xFF || decoder.Temp[1] != expectedRst)
- {
- bool invalidRst = true;
-
- // Most jpeg's containing well-formed input will have a RST[0-7] marker following immediately
- // but some, see Issue #481, contain padding bytes "0xFF" before the RST[0-7] marker.
- // If we identify that case we attempt to read until we have bypassed the padded bytes.
- // We then check again for our RST marker and throw if invalid.
- // No other methods are attempted to resynchronize from corrupt input.
- if (decoder.Temp[0] == 0xFF && decoder.Temp[1] == 0xFF)
- {
- while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError())
- {
- decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1);
- if (!decoder.InputProcessor.CheckEOFEnsureNoError())
- {
- break;
- }
- }
-
- // Have we found a valid restart marker?
- invalidRst = decoder.Temp[0] != expectedRst;
- }
-
- if (invalidRst)
- {
- throw new ImageFormatException("Bad RST marker");
- }
- }
-
- expectedRst++;
- if (expectedRst == OrigJpegConstants.Markers.RST7 + 1)
+ decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1);
+ if (!decoder.InputProcessor.CheckEOFEnsureNoError())
{
- expectedRst = OrigJpegConstants.Markers.RST0;
+ break;
}
}
- }
- // Reset the Huffman decoder.
- decoder.InputProcessor.Bits = default(Bits);
+ // Have we found a valid restart marker?
+ invalidRst = decoder.Temp[0] != this.expectedRst;
+ }
- // Reset the DC components, as per section F.2.1.3.1.
- this.ResetDc();
+ if (invalidRst)
+ {
+ throw new ImageFormatException("Bad RST marker");
+ }
+ }
- // Reset the progressive decoder state, as per section G.1.2.2.
- this.eobRun = 0;
+ this.expectedRst++;
+ if (this.expectedRst == OrigJpegConstants.Markers.RST7 + 1)
+ {
+ this.expectedRst = OrigJpegConstants.Markers.RST0;
}
}
-
- // for mx
}
}
- private void ResetDc()
+ private void Reset(OrigJpegDecoderCore decoder)
+ {
+ decoder.InputProcessor.ResetHuffmanDecoder();
+
+ this.ResetDcValues();
+
+ // Reset the progressive decoder state, as per section G.1.2.2.
+ this.eobRun = 0;
+ }
+
+ ///
+ /// Reset the DC components, as per section F.2.1.3.1.
+ ///
+ private void ResetDcValues()
{
Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * OrigJpegDecoderCore.MaxComponents);
}
diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
index 58513fd297..6cc275d49d 100644
--- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
@@ -411,6 +411,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.InitDerivedMetaDataProperties();
}
+ ///
+ /// Returns true if 'mcuCounter' is at restart interval
+ ///
+ public bool IsAtRestartInterval(int mcuCounter)
+ {
+ return this.RestartInterval > 0 && mcuCounter % this.RestartInterval == 0
+ && mcuCounter < this.TotalMCUCount;
+ }
+
///
/// Assigns derived metadata properties to , eg. horizontal and vertical resolution if it has a JFIF header.
///