Browse Source

fixed #18

af/merge-core
Anton Firszov 9 years ago
parent
commit
ea107ce2ff
  1. 24
      src/ImageSharp.Formats.Jpeg/Components/Decoder/BufferProcessor.cs
  2. 108
      src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs
  3. 4
      src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs
  4. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs

24
src/ImageSharp.Formats.Jpeg/Components/Decoder/BufferProcessor.cs

@ -50,7 +50,7 @@ namespace ImageSharp.Formats.Jpg
public byte[] Temp { get; } public byte[] Temp { get; }
/// <summary> /// <summary>
/// Gets or sets the value indicating whether an unexpected EOF reached in <see cref="InputStream"/>. /// Gets or sets a value indicating whether an unexpected EOF reached in <see cref="InputStream"/>.
/// </summary> /// </summary>
public bool UnexpectedEndOfStreamReached { get; set; } public bool UnexpectedEndOfStreamReached { get; set; }
@ -58,8 +58,8 @@ namespace ImageSharp.Formats.Jpg
/// If errorCode indicates unexpected EOF, sets <see cref="UnexpectedEndOfStreamReached"/> to true and returns false. /// If errorCode indicates unexpected EOF, sets <see cref="UnexpectedEndOfStreamReached"/> to true and returns false.
/// Calls <see cref="DecoderThrowHelper.EnsureNoError"/> and returns true otherwise. /// Calls <see cref="DecoderThrowHelper.EnsureNoError"/> and returns true otherwise.
/// </summary> /// </summary>
/// <param name="errorCode"></param> /// <param name="errorCode">The <see cref="DecoderErrorCode"/></param>
/// <returns></returns> /// <returns><see cref="bool"/> indicating whether everything is OK</returns>
public bool CheckEOFEnsureNoError(DecoderErrorCode errorCode) public bool CheckEOFEnsureNoError(DecoderErrorCode errorCode)
{ {
if (errorCode == DecoderErrorCode.UnexpectedEndOfStream) if (errorCode == DecoderErrorCode.UnexpectedEndOfStream)
@ -67,10 +67,28 @@ namespace ImageSharp.Formats.Jpg
this.UnexpectedEndOfStreamReached = true; this.UnexpectedEndOfStreamReached = true;
return false; return false;
} }
errorCode.EnsureNoError(); errorCode.EnsureNoError();
return true; return true;
} }
/// <summary>
/// If errorCode indicates unexpected EOF, sets <see cref="UnexpectedEndOfStreamReached"/> to true and returns false.
/// Returns true otherwise.
/// </summary>
/// <param name="errorCode">The <see cref="DecoderErrorCode"/></param>
/// <returns><see cref="bool"/> indicating whether everything is OK</returns>
public bool CheckEOF(DecoderErrorCode errorCode)
{
if (errorCode == DecoderErrorCode.UnexpectedEndOfStream)
{
this.UnexpectedEndOfStreamReached = true;
return false;
}
return true;
}
/// <summary> /// <summary>
/// Dispose /// Dispose
/// </summary> /// </summary>

108
src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs

@ -198,7 +198,10 @@ namespace ImageSharp.Formats.Jpg
int blockIndex = this.GetBlockIndex(decoder); int blockIndex = this.GetBlockIndex(decoder);
this.data.Block = decoder.DecodedBlocks[this.ComponentIndex][blockIndex].Block; this.data.Block = decoder.DecodedBlocks[this.ComponentIndex][blockIndex].Block;
this.DecodeBlock(decoder, scanIndex); if (!decoder.BufferProcessor.UnexpectedEndOfStreamReached)
{
this.DecodeBlock(decoder, scanIndex);
}
// Store the decoded block // Store the decoded block
DecodedBlockMemento[] blocks = decoder.DecodedBlocks[this.ComponentIndex]; DecodedBlockMemento[] blocks = decoder.DecodedBlocks[this.ComponentIndex];
@ -215,17 +218,22 @@ namespace ImageSharp.Formats.Jpg
{ {
// A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input, // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input,
// but this one assumes well-formed input, and hence the restart marker follows immediately. // but this one assumes well-formed input, and hence the restart marker follows immediately.
decoder.BufferProcessor.ReadFull(decoder.Temp, 0, 2); if (!decoder.BufferProcessor.UnexpectedEndOfStreamReached)
if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst)
{ {
throw new ImageFormatException("Bad RST marker"); DecoderErrorCode errorCode = decoder.BufferProcessor.ReadFullUnsafe(decoder.Temp, 0, 2);
} if (decoder.BufferProcessor.CheckEOFEnsureNoError(errorCode))
{
if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst)
{
throw new ImageFormatException("Bad RST marker");
}
expectedRst++; expectedRst++;
if (expectedRst == JpegConstants.Markers.RST7 + 1) if (expectedRst == JpegConstants.Markers.RST7 + 1)
{ {
expectedRst = JpegConstants.Markers.RST0; expectedRst = JpegConstants.Markers.RST0;
}
}
} }
// Reset the Huffman decoder. // Reset the Huffman decoder.
@ -362,7 +370,10 @@ namespace ImageSharp.Formats.Jpg
errorCode = decoder.BufferProcessor.DecodeHuffmanUnsafe( errorCode = decoder.BufferProcessor.DecodeHuffmanUnsafe(
ref decoder.HuffmanTrees[huffmanIndex], ref decoder.HuffmanTrees[huffmanIndex],
out value); out value);
errorCode.EnsureNoEOF(); if (!decoder.BufferProcessor.CheckEOF(errorCode))
{
return;
}
if (value > 16) if (value > 16)
{ {
@ -371,7 +382,10 @@ namespace ImageSharp.Formats.Jpg
int deltaDC; int deltaDC;
errorCode = decoder.BufferProcessor.ReceiveExtendUnsafe(value, out deltaDC); errorCode = decoder.BufferProcessor.ReceiveExtendUnsafe(value, out deltaDC);
errorCode.EnsureNoError(); if (!decoder.BufferProcessor.CheckEOFEnsureNoError(errorCode))
{
return;
}
this.pointers.Dc[this.ComponentIndex] += deltaDC; this.pointers.Dc[this.ComponentIndex] += deltaDC;
@ -390,7 +404,10 @@ namespace ImageSharp.Formats.Jpg
{ {
int value; int value;
errorCode = decoder.BufferProcessor.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value); errorCode = decoder.BufferProcessor.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value);
errorCode.EnsureNoEOF(); if (!decoder.BufferProcessor.CheckEOF(errorCode))
{
return;
}
int val0 = value >> 4; int val0 = value >> 4;
int val1 = value & 0x0f; int val1 = value & 0x0f;
@ -404,7 +421,10 @@ namespace ImageSharp.Formats.Jpg
int ac; int ac;
errorCode = decoder.BufferProcessor.ReceiveExtendUnsafe(val1, out ac); errorCode = decoder.BufferProcessor.ReceiveExtendUnsafe(val1, out ac);
errorCode.EnsureNoError(); if (!decoder.BufferProcessor.CheckEOFEnsureNoError(errorCode))
{
return;
}
// b[Unzig[zig]] = ac << al; // b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al); Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al);
@ -417,7 +437,10 @@ namespace ImageSharp.Formats.Jpg
if (val0 != 0) if (val0 != 0)
{ {
errorCode = this.DecodeEobRun(val0, ref decoder.BufferProcessor); errorCode = this.DecodeEobRun(val0, ref decoder.BufferProcessor);
errorCode.EnsureNoError(); if (!decoder.BufferProcessor.CheckEOFEnsureNoError(errorCode))
{
return;
}
} }
this.eobRun--; this.eobRun--;
@ -428,7 +451,7 @@ namespace ImageSharp.Formats.Jpg
} }
} }
} }
} }
} }
private DecoderErrorCode DecodeEobRun(int count, ref BufferProcessor decoder) private DecoderErrorCode DecodeEobRun(int count, ref BufferProcessor decoder)
@ -516,10 +539,10 @@ namespace ImageSharp.Formats.Jpg
/// <summary> /// <summary>
/// Decodes a successive approximation refinement block, as specified in section G.1.2. /// Decodes a successive approximation refinement block, as specified in section G.1.2.
/// </summary> /// </summary>
/// <param name="decoder">The decoder instance</param> /// <param name="bp">The <see cref="BufferProcessor"/> instance</param>
/// <param name="h">The Huffman tree</param> /// <param name="h">The Huffman tree</param>
/// <param name="delta">The low transform offset</param> /// <param name="delta">The low transform offset</param>
private void Refine(ref BufferProcessor decoder, ref HuffmanTree h, int delta) private void Refine(ref BufferProcessor bp, ref HuffmanTree h, int delta)
{ {
Block8x8F* b = this.pointers.Block; Block8x8F* b = this.pointers.Block;
@ -532,8 +555,12 @@ namespace ImageSharp.Formats.Jpg
} }
bool bit; bool bit;
DecoderErrorCode errorCode = decoder.DecodeBitUnsafe(out bit); DecoderErrorCode errorCode = bp.DecodeBitUnsafe(out bit);
errorCode.EnsureNoError(); if (!bp.CheckEOFEnsureNoError(errorCode))
{
return;
}
if (bit) if (bit)
{ {
int stuff = (int)Block8x8F.GetScalarAt(b, 0); int stuff = (int)Block8x8F.GetScalarAt(b, 0);
@ -558,8 +585,11 @@ namespace ImageSharp.Formats.Jpg
int z = 0; int z = 0;
int val; int val;
DecoderErrorCode errorCode = decoder.DecodeHuffmanUnsafe(ref h, out val); DecoderErrorCode errorCode = bp.DecodeHuffmanUnsafe(ref h, out val);
errorCode.EnsureNoEOF(); if (!bp.CheckEOF(errorCode))
{
return;
}
int val0 = val >> 4; int val0 = val >> 4;
int val1 = val & 0x0f; int val1 = val & 0x0f;
@ -572,8 +602,11 @@ namespace ImageSharp.Formats.Jpg
this.eobRun = 1 << val0; this.eobRun = 1 << val0;
if (val0 != 0) if (val0 != 0)
{ {
errorCode = this.DecodeEobRun(val0, ref decoder); errorCode = this.DecodeEobRun(val0, ref bp);
errorCode.EnsureNoError(); if (!bp.CheckEOFEnsureNoError(errorCode))
{
return;
}
} }
done = true; done = true;
@ -584,8 +617,11 @@ namespace ImageSharp.Formats.Jpg
z = delta; z = delta;
bool bit; bool bit;
errorCode = decoder.DecodeBitUnsafe(out bit); errorCode = bp.DecodeBitUnsafe(out bit);
errorCode.EnsureNoError(); if (!bp.CheckEOFEnsureNoError(errorCode))
{
return;
}
if (!bit) if (!bit)
{ {
@ -602,7 +638,12 @@ namespace ImageSharp.Formats.Jpg
break; break;
} }
zig = this.RefineNonZeroes(ref decoder, zig, val0, delta); zig = this.RefineNonZeroes(ref bp, zig, val0, delta);
if (bp.UnexpectedEndOfStreamReached)
{
return;
}
if (zig > this.zigEnd) if (zig > this.zigEnd)
{ {
throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}"); throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}");
@ -619,7 +660,7 @@ namespace ImageSharp.Formats.Jpg
if (this.eobRun > 0) if (this.eobRun > 0)
{ {
this.eobRun--; this.eobRun--;
this.RefineNonZeroes(ref decoder, zig, -1, delta); this.RefineNonZeroes(ref bp, zig, -1, delta);
} }
} }
@ -627,12 +668,12 @@ namespace ImageSharp.Formats.Jpg
/// Refines non-zero entries of b in zig-zag order. /// Refines non-zero entries of b in zig-zag order.
/// If <paramref name="nz" /> >= 0, the first <paramref name="nz" /> zero entries are skipped over. /// If <paramref name="nz" /> >= 0, the first <paramref name="nz" /> zero entries are skipped over.
/// </summary> /// </summary>
/// <param name="decoder">The decoder</param> /// <param name="bp">The <see cref="BufferProcessor"/></param>
/// <param name="zig">The zig-zag start index</param> /// <param name="zig">The zig-zag start index</param>
/// <param name="nz">The non-zero entry</param> /// <param name="nz">The non-zero entry</param>
/// <param name="delta">The low transform offset</param> /// <param name="delta">The low transform offset</param>
/// <returns>The <see cref="int" /></returns> /// <returns>The <see cref="int" /></returns>
private int RefineNonZeroes(ref BufferProcessor decoder, int zig, int nz, int delta) private int RefineNonZeroes(ref BufferProcessor bp, int zig, int nz, int delta)
{ {
var b = this.pointers.Block; var b = this.pointers.Block;
for (; zig <= this.zigEnd; zig++) for (; zig <= this.zigEnd; zig++)
@ -653,8 +694,11 @@ namespace ImageSharp.Formats.Jpg
} }
bool bit; bool bit;
DecoderErrorCode errorCode = decoder.DecodeBitUnsafe(out bit); DecoderErrorCode errorCode = bp.DecodeBitUnsafe(out bit);
errorCode.EnsureNoError(); if (!bp.CheckEOFEnsureNoError(errorCode))
{
return int.MinValue;
}
if (!bit) if (!bit)
{ {

4
src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs

@ -397,9 +397,9 @@ namespace ImageSharp.Formats
// when this is a progressive image this gets called a number of times // when this is a progressive image this gets called a number of times
// need to know how many times this should be called in total. // need to know how many times this should be called in total.
this.ProcessStartOfScan(remaining); this.ProcessStartOfScan(remaining);
if (!this.IsProgressive) if (this.BufferProcessor.UnexpectedEndOfStreamReached || !this.IsProgressive)
{ {
// if this is not a progressive image we can stop processing bytes as we now have the image data. // if unexpeced EOF reached or this is not a progressive image we can stop processing bytes as we now have the image data.
processBytes = false; processBytes = false;
} }

2
tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs

@ -32,7 +32,7 @@ namespace ImageSharp.Tests
TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Jpeg444,
}; };
[Theory] // Benchmark, enable manually // [Theory] // Benchmark, enable manually
[MemberData(nameof(DecodeJpegData))] [MemberData(nameof(DecodeJpegData))]
public void DecodeJpeg(string fileName) public void DecodeJpeg(string fileName)
{ {

Loading…
Cancel
Save