diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs
index 88aa8a3fe..8e46e1806 100644
--- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs
+++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs
@@ -39,11 +39,11 @@ namespace ImageSharp.Formats.Jpg
/// Jpeg decoder
/// Error code
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal JpegDecoderCore.ErrorCodes EnsureNBits(int n, JpegDecoderCore decoder)
+ internal DecoderErrorCode EnsureNBits(int n, JpegDecoderCore decoder)
{
while (true)
{
- JpegDecoderCore.ErrorCodes errorCode;
+ DecoderErrorCode errorCode;
// Grab the decode bytes, use them and then set them
// back on the decoder.
@@ -51,7 +51,7 @@ namespace ImageSharp.Formats.Jpg
byte c = decoderBytes.ReadByteStuffedByte(decoder.InputStream, out errorCode);
decoder.Bytes = decoderBytes;
- if (errorCode != JpegDecoderCore.ErrorCodes.NoError)
+ if (errorCode != DecoderErrorCode.NoError)
{
return errorCode;
}
@@ -69,7 +69,7 @@ namespace ImageSharp.Formats.Jpg
if (this.UnreadBits >= n)
{
- return JpegDecoderCore.ErrorCodes.NoError;
+ return DecoderErrorCode.NoError;
}
}
}
@@ -84,11 +84,8 @@ namespace ImageSharp.Formats.Jpg
{
if (this.UnreadBits < t)
{
- JpegDecoderCore.ErrorCodes errorCode = this.EnsureNBits(t, decoder);
- if (errorCode != JpegDecoderCore.ErrorCodes.NoError)
- {
- throw new JpegDecoderCore.MissingFF00Exception();
- }
+ DecoderErrorCode errorCode = this.EnsureNBits(t, decoder);
+ errorCode.EnsureNoError();
}
this.UnreadBits -= t;
diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs
index b91420b42..e33f852ff 100644
--- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs
+++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs
@@ -69,11 +69,11 @@ namespace ImageSharp.Formats.Jpg
/// Input stream
/// Error code
/// The
- internal byte ReadByteStuffedByte(Stream inputStream, out JpegDecoderCore.ErrorCodes errorCode)
+ internal byte ReadByteStuffedByte(Stream inputStream, out DecoderErrorCode errorCode)
{
byte x;
- errorCode = JpegDecoderCore.ErrorCodes.NoError;
+ errorCode = DecoderErrorCode.NoError;
// Take the fast path if bytes.buf contains at least two bytes.
if (this.I + 2 <= this.J)
@@ -88,7 +88,7 @@ namespace ImageSharp.Formats.Jpg
if (this.Buffer[this.I] != 0x00)
{
- errorCode = JpegDecoderCore.ErrorCodes.MissingFF00;
+ errorCode = DecoderErrorCode.MissingFF00;
return 0;
// throw new MissingFF00Exception();
@@ -112,7 +112,7 @@ namespace ImageSharp.Formats.Jpg
this.UnreadableBytes = 2;
if (x != 0x00)
{
- errorCode = JpegDecoderCore.ErrorCodes.MissingFF00;
+ errorCode = DecoderErrorCode.MissingFF00;
return 0;
// throw new MissingFF00Exception();
@@ -167,7 +167,7 @@ namespace ImageSharp.Formats.Jpg
int n = inputStream.Read(this.Buffer, this.J, this.Buffer.Length - this.J);
if (n == 0)
{
- throw new JpegDecoderCore.EOFException();
+ throw new EOFException();
}
this.J += n;
diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderErrorCode.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderErrorCode.cs
new file mode 100644
index 000000000..4adc9fa7f
--- /dev/null
+++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderErrorCode.cs
@@ -0,0 +1,29 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ ///
+ /// Represents "recoverable" decoder errors.
+ ///
+ internal enum DecoderErrorCode
+ {
+ ///
+ /// NoError
+ ///
+ NoError,
+
+ ///
+ /// MissingFF00
+ ///
+ // ReSharper disable once InconsistentNaming
+ MissingFF00,
+
+ ///
+ /// End of stream reached unexpectedly
+ ///
+ UnexpectedEndOfFile
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs
new file mode 100644
index 000000000..33321bff9
--- /dev/null
+++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs
@@ -0,0 +1,49 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats.Jpg
+{
+ using System;
+ using System.Runtime.CompilerServices;
+
+ ///
+ /// Encapsulates exception thrower methods for the Jpeg Encoder
+ ///
+ internal static class DecoderThrowHelper
+ {
+ ///
+ /// Throws an exception that belongs to the given
+ ///
+ /// The
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void ThrowExceptionForErrorCode(this DecoderErrorCode errorCode)
+ {
+ switch (errorCode)
+ {
+ case DecoderErrorCode.NoError:
+ throw new ArgumentException("ThrowExceptionForErrorCode() called with NoError!", nameof(errorCode));
+ case DecoderErrorCode.MissingFF00:
+ throw new MissingFF00Exception();
+ case DecoderErrorCode.UnexpectedEndOfFile:
+ throw new EOFException();
+ default:
+ throw new ArgumentOutOfRangeException(nameof(errorCode), errorCode, null);
+ }
+ }
+
+ ///
+ /// Throws an exception if the given defines an error.
+ ///
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void EnsureNoError(this DecoderErrorCode errorCode)
+ {
+ if (errorCode != DecoderErrorCode.NoError)
+ {
+ ThrowExceptionForErrorCode(errorCode);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/EOFException.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/EOFException.cs
new file mode 100644
index 000000000..81857b456
--- /dev/null
+++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/EOFException.cs
@@ -0,0 +1,21 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats.Jpg
+{
+ using System;
+
+ ///
+ /// The EOF (End of File exception).
+ /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker
+ ///
+ internal class EOFException : Exception
+ {
+ public EOFException()
+ : base("Reached end of stream before proceeding EOI marker!")
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs
index 9fef5010d..8c2f07970 100644
--- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs
+++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs
@@ -127,9 +127,9 @@ namespace ImageSharp.Formats.Jpg
{
for (int mx = 0; mx < decoder.MCUCountX; mx++)
{
- for (int i = 0; i < this.componentScanCount; i++)
+ for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++)
{
- this.componentIndex = this.pointers.ComponentScan[i].ComponentIndex;
+ this.componentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex;
this.hi = decoder.ComponentArray[this.componentIndex].HorizontalFactor;
int vi = decoder.ComponentArray[this.componentIndex].VerticalFactor;
@@ -190,7 +190,7 @@ namespace ImageSharp.Formats.Jpg
this.data.Block.Clear();
}
- this.ProcessBlockImpl(decoder, i);
+ this.ProcessBlockImpl(decoder, scanIndex);
}
// for j
@@ -204,6 +204,7 @@ namespace ImageSharp.Formats.Jpg
// 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.
decoder.ReadFull(decoder.Temp, 0, 2);
+
if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst)
{
throw new ImageFormatException("Bad RST marker");
@@ -306,12 +307,12 @@ namespace ImageSharp.Formats.Jpg
/// Process the current block at (, )
///
/// The decoder
- /// The index of the scan
- private void ProcessBlockImpl(JpegDecoderCore decoder, int i)
+ /// The index of the scan
+ private void ProcessBlockImpl(JpegDecoderCore decoder, int scanIndex)
{
var b = this.pointers.Block;
- int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[i].AcTableSelector;
+ int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector;
if (this.ah != 0)
{
this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al);
@@ -319,6 +320,7 @@ namespace ImageSharp.Formats.Jpg
else
{
int zig = this.zigStart;
+ DecoderErrorCode errorCode;
if (zig == 0)
{
zig++;
@@ -326,13 +328,15 @@ namespace ImageSharp.Formats.Jpg
// Decode the DC coefficient, as specified in section F.2.2.1.
byte value =
decoder.DecodeHuffman(
- ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[i].DcTableSelector]);
+ ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector]);
if (value > 16)
{
throw new ImageFormatException("Excessive DC component");
}
int deltaDC = decoder.Bits.ReceiveExtend(value, decoder);
+ // errorCode.EnsureNoError();
+
this.pointers.Dc[this.componentIndex] += deltaDC;
// b[0] = dc[compIndex] << al;
@@ -360,6 +364,7 @@ namespace ImageSharp.Formats.Jpg
}
int ac = decoder.Bits.ReceiveExtend(val1, decoder);
+ // errorCode.EnsureNoError();
// b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al);
diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/MissingFF00Exception.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/MissingFF00Exception.cs
new file mode 100644
index 000000000..f8c157237
--- /dev/null
+++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/MissingFF00Exception.cs
@@ -0,0 +1,17 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats.Jpg
+{
+ using System;
+
+ ///
+ /// The missing ff00 exception.
+ ///
+ // ReSharper disable once InconsistentNaming
+ internal class MissingFF00Exception : Exception
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs
index 707f9d3e4..3bb774b69 100644
--- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs
@@ -93,24 +93,6 @@ namespace ImageSharp.Formats
this.Bytes = Bytes.Create();
}
- ///
- /// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent)
- /// It's better tho have an error code for this!
- ///
- internal enum ErrorCodes
- {
- ///
- /// NoError
- ///
- NoError,
-
- ///
- /// MissingFF00
- ///
- // ReSharper disable once InconsistentNaming
- MissingFF00
- }
-
///
/// Gets the component array
///
@@ -437,7 +419,8 @@ namespace ImageSharp.Formats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte ReadByte()
{
- return this.Bytes.ReadByte(this.InputStream);
+ byte result = this.Bytes.ReadByte(this.InputStream);
+ return result;
}
///
@@ -448,11 +431,8 @@ namespace ImageSharp.Formats
{
if (this.Bits.UnreadBits == 0)
{
- ErrorCodes errorCode = this.Bits.EnsureNBits(1, this);
- if (errorCode != ErrorCodes.NoError)
- {
- throw new MissingFF00Exception();
- }
+ DecoderErrorCode errorCode = this.Bits.EnsureNBits(1, this);
+ errorCode.EnsureNoError();
}
bool ret = (this.Bits.Accumulator & this.Bits.Mask) != 0;
@@ -467,7 +447,22 @@ namespace ImageSharp.Formats
/// The data to write to.
/// The offset in the source buffer
/// The number of bytes to read
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadFull(byte[] data, int offset, int length)
+ {
+ DecoderErrorCode errorCode = this.ReadFullUnsafe(data, offset, length);
+ errorCode.EnsureNoError();
+ }
+
+ ///
+ /// Reads exactly length bytes into data. It does not care about byte stuffing.
+ /// Does not throw on errors, returns instead!
+ ///
+ /// The data to write to.
+ /// The offset in the source buffer
+ /// The number of bytes to read
+ /// The
+ public DecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length)
{
// Unread the overshot bytes, if any.
if (this.Bytes.UnreadableBytes != 0)
@@ -498,7 +493,9 @@ namespace ImageSharp.Formats
this.Bytes.Fill(this.InputStream);
}
}
- }
+
+ return DecoderErrorCode.NoError;
+ }
///
/// Decodes the given number of bits
@@ -509,11 +506,8 @@ namespace ImageSharp.Formats
{
if (this.Bits.UnreadBits < count)
{
- ErrorCodes errorCode = this.Bits.EnsureNBits(count, this);
- if (errorCode != ErrorCodes.NoError)
- {
- throw new MissingFF00Exception();
- }
+ DecoderErrorCode errorCode = this.Bits.EnsureNBits(count, this);
+ errorCode.EnsureNoError();
}
uint ret = this.Bits.Accumulator >> (this.Bits.UnreadBits - count);
@@ -538,9 +532,9 @@ namespace ImageSharp.Formats
if (this.Bits.UnreadBits < 8)
{
- ErrorCodes errorCode = this.Bits.EnsureNBits(8, this);
+ DecoderErrorCode errorCode = this.Bits.EnsureNBits(8, this);
- if (errorCode == ErrorCodes.NoError)
+ if (errorCode == DecoderErrorCode.NoError)
{
ushort v = huffmanTree.Lut[(this.Bits.Accumulator >> (this.Bits.UnreadBits - HuffmanTree.LutSize)) & 0xFF];
@@ -552,6 +546,10 @@ namespace ImageSharp.Formats
return (byte)(v >> 8);
}
}
+ else if (errorCode == DecoderErrorCode.UnexpectedEndOfFile)
+ {
+ errorCode.ThrowExceptionForErrorCode();
+ }
else
{
this.UnreadByteStuffedByte();
@@ -563,11 +561,8 @@ namespace ImageSharp.Formats
{
if (this.Bits.UnreadBits == 0)
{
- ErrorCodes errorCode = this.Bits.EnsureNBits(1, this);
- if (errorCode != ErrorCodes.NoError)
- {
- throw new MissingFF00Exception();
- }
+ DecoderErrorCode errorCode = this.Bits.EnsureNBits(1, this);
+ errorCode.EnsureNoError();
}
if ((this.Bits.Accumulator & this.Bits.Mask) != 0)
@@ -1440,7 +1435,20 @@ namespace ImageSharp.Formats
/// Skips the next n bytes.
///
/// The number of bytes to ignore.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Skip(int count)
+ {
+ DecoderErrorCode errorCode = this.SkipUnsafe(count);
+ errorCode.EnsureNoError();
+ }
+
+ ///
+ /// Skips the next n bytes.
+ /// Does not throw, returns instead!
+ ///
+ /// The number of bytes to ignore.
+ /// The
+ private DecoderErrorCode SkipUnsafe(int count)
{
// Unread the overshot bytes, if any.
if (this.Bytes.UnreadableBytes != 0)
@@ -1470,6 +1478,8 @@ namespace ImageSharp.Formats
this.Bytes.Fill(this.InputStream);
}
+
+ return DecoderErrorCode.NoError;
}
///
@@ -1490,21 +1500,5 @@ namespace ImageSharp.Formats
this.Bits.Mask >>= 8;
}
}
-
- ///
- /// The EOF (End of File exception).
- /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker
- ///
- internal class EOFException : Exception
- {
- }
-
- ///
- /// The missing ff00 exception.
- ///
- // ReSharper disable once InconsistentNaming
- internal class MissingFF00Exception : Exception
- {
- }
}
-}
\ No newline at end of file
+}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs
index 94ae9a2b1..628bc4ea9 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs
@@ -36,7 +36,7 @@ namespace ImageSharp.Tests
provider.Utility.SaveTestOutputFile(image, "bmp");
}
- // [Theory] // TODO: #18
+ [Theory] // TODO: #18
[WithFile(TestImages.Jpeg.Progressive.Bad.BadEOF, PixelTypes.Color)]
public void LoadProgressiveImage(TestImageProvider provider)
where TColor : struct, IPackedPixel, IEquatable