diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs index ab08b0d4cf..36344b75a6 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs @@ -94,24 +94,45 @@ namespace ImageSharp.Formats.Jpg /// Byte /// Jpeg decoder /// Read bits value - internal int ReceiveExtend(byte t, JpegDecoderCore decoder) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReceiveExtend(byte t, JpegDecoderCore decoder) + { + int x; + DecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, decoder, out x); + errorCode.EnsureNoError(); + return x; + } + + /// + /// Receive extend + /// + /// Byte + /// Jpeg decoder + /// Read bits value + /// The + public DecoderErrorCode ReceiveExtendUnsafe(byte t, JpegDecoderCore decoder, out int x) { if (this.UnreadBits < t) { - this.EnsureNBits(t, decoder); + DecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, decoder); + if (errorCode != DecoderErrorCode.NoError) + { + x = int.MaxValue; + return errorCode; + } } this.UnreadBits -= t; this.Mask >>= t; int s = 1 << t; - int x = (int)((this.Accumulator >> this.UnreadBits) & (s - 1)); + x = (int)((this.Accumulator >> this.UnreadBits) & (s - 1)); if (x < (s >> 1)) { x += ((-1) << t) + 1; } - return x; + return DecoderErrorCode.NoError; } } } \ No newline at end of file diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs index 176e0cfbeb..6ca017f4e8 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs @@ -101,6 +101,7 @@ namespace ImageSharp.Formats.Jpg { return errorCode; } + if (x != JpegConstants.Markers.XFF) { return DecoderErrorCode.NoError; @@ -112,6 +113,7 @@ namespace ImageSharp.Formats.Jpg { return errorCode; } + if (x != 0x00) { return DecoderErrorCode.MissingFF00; diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs index 18ba02a9a0..84f7579c8a 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs @@ -46,18 +46,40 @@ namespace ImageSharp.Formats.Jpg } } + /// + /// Throws an exception if the given is . + /// + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void EnsureNoEOF(this DecoderErrorCode errorCode) + { + if (errorCode == DecoderErrorCode.UnexpectedEndOfStream) + { + errorCode.ThrowExceptionForErrorCode(); + } + } + /// /// Encapsulates methods throwing different flavours of -s. /// public static class ThrowImageFormatException { /// - /// Throws "Fill called when unread bytes exist." + /// Throws "Fill called when unread bytes exist". /// [MethodImpl(MethodImplOptions.NoInlining)] public static void FillCalledWhenUnreadBytesExist() { - throw new ImageFormatException("Fill called when unread bytes exist."); + throw new ImageFormatException("Fill called when unread bytes exist!"); + } + + /// + /// Throws "Bad Huffman code". + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static void BadHuffmanCode() + { + throw new ImageFormatException("Bad Huffman code!"); } } } diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs index ef2ce18883..30cb6c851b 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs @@ -308,7 +308,7 @@ namespace ImageSharp.Formats.Jpg private void ProcessBlockImpl(JpegDecoderCore decoder, int scanIndex) { var b = this.pointers.Block; - + DecoderErrorCode errorCode; int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; if (this.ah != 0) { @@ -322,9 +322,13 @@ namespace ImageSharp.Formats.Jpg zig++; // 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[scanIndex].DcTableSelector]); + byte value; + int huffmanIndex = (DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; + errorCode = decoder.DecodeHuffmanUnsafe( + ref decoder.HuffmanTrees[huffmanIndex], + out value); + errorCode.EnsureNoEOF(); + if (value > 16) { throw new ImageFormatException("Excessive DC component"); @@ -347,7 +351,10 @@ namespace ImageSharp.Formats.Jpg // Decode the AC coefficients, as specified in section F.2.2.2. for (; zig <= this.zigEnd; zig++) { - byte value = decoder.DecodeHuffman(ref decoder.HuffmanTrees[huffmannIdx]); + byte value; + errorCode = decoder.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value); + errorCode.EnsureNoEOF(); + byte val0 = (byte)(value >> 4); byte val1 = (byte)(value & 0x0f); if (val1 != 0) @@ -370,7 +377,8 @@ namespace ImageSharp.Formats.Jpg this.eobRun = (ushort)(1 << val0); if (val0 != 0) { - this.eobRun |= (ushort)decoder.DecodeBits(val0); + errorCode = this.DecodeEobRun(val0, decoder); + errorCode.EnsureNoError(); } this.eobRun--; @@ -409,6 +417,19 @@ namespace ImageSharp.Formats.Jpg destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2); } + private DecoderErrorCode DecodeEobRun(int count, JpegDecoderCore decoder) + { + uint bitsResult; + DecoderErrorCode errorCode = decoder.DecodeBitsUnsafe(count, out bitsResult); + if (errorCode != DecoderErrorCode.NoError) + { + return errorCode; + } + + this.eobRun |= (ushort)bitsResult; + return DecoderErrorCode.NoError; + } + /// /// Gets the block index used to retieve blocks from in /// @@ -496,7 +517,9 @@ namespace ImageSharp.Formats.Jpg throw new ImageFormatException("Invalid state for zig DC component"); } - bool bit = decoder.DecodeBit(); + bool bit; + DecoderErrorCode errorCode = decoder.DecodeBitUnsafe(out bit); + errorCode.EnsureNoError(); if (bit) { int stuff = (int)Block8x8F.GetScalarAt(b, 0); @@ -519,7 +542,11 @@ namespace ImageSharp.Formats.Jpg { bool done = false; int z = 0; - byte val = decoder.DecodeHuffman(ref h); + + byte val; + DecoderErrorCode errorCode = decoder.DecodeHuffmanUnsafe(ref h, out val); + errorCode.EnsureNoEOF(); + int val0 = val >> 4; int val1 = val & 0x0f; @@ -531,7 +558,8 @@ namespace ImageSharp.Formats.Jpg this.eobRun = (ushort)(1 << val0); if (val0 != 0) { - this.eobRun |= (ushort)decoder.DecodeBits(val0); + errorCode = this.DecodeEobRun(val0, decoder); + errorCode.EnsureNoError(); } done = true; @@ -540,7 +568,11 @@ namespace ImageSharp.Formats.Jpg break; case 1: z = delta; - bool bit = decoder.DecodeBit(); + + bool bit; + errorCode = decoder.DecodeBitUnsafe(out bit); + errorCode.EnsureNoError(); + if (!bit) { z = -z; @@ -606,7 +638,10 @@ namespace ImageSharp.Formats.Jpg continue; } - bool bit = decoder.DecodeBit(); + bool bit; + DecoderErrorCode errorCode = decoder.DecodeBitUnsafe(out bit); + errorCode.EnsureNoError(); + if (!bit) { continue; diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs index 5571ba7075..1e1210387a 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs @@ -435,25 +435,31 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public byte ReadByte() { - byte result = this.Bytes.ReadByte(this.InputStream); - return result; + return this.Bytes.ReadByte(this.InputStream); } /// /// Decodes a single bit + /// TODO: This method (and also the usages) could be optimized by batching! /// - /// The - public bool DecodeBit() + /// The decoded bit as a + /// The + public DecoderErrorCode DecodeBitUnsafe(out bool result) { if (this.Bits.UnreadBits == 0) { - this.Bits.EnsureNBits(1, this); + DecoderErrorCode errorCode = this.Bits.EnsureNBitsUnsafe(1, this); + if (errorCode != DecoderErrorCode.NoError) + { + result = false; + return errorCode; + } } - bool ret = (this.Bits.Accumulator & this.Bits.Mask) != 0; + result = (this.Bits.Accumulator & this.Bits.Mask) != 0; this.Bits.UnreadBits--; this.Bits.Mask >>= 1; - return ret; + return DecoderErrorCode.NoError; } /// @@ -517,28 +523,32 @@ namespace ImageSharp.Formats /// Decodes the given number of bits /// /// The number of bits to decode. - /// The - public uint DecodeBits(int count) + /// The result + /// The + public DecoderErrorCode DecodeBitsUnsafe(int count, out uint result) { if (this.Bits.UnreadBits < count) { this.Bits.EnsureNBits(count, this); } - uint ret = this.Bits.Accumulator >> (this.Bits.UnreadBits - count); - ret = (uint)(ret & ((1 << count) - 1)); + result = this.Bits.Accumulator >> (this.Bits.UnreadBits - count); + result = (uint)(result & ((1 << count) - 1)); this.Bits.UnreadBits -= count; this.Bits.Mask >>= count; - return ret; + return DecoderErrorCode.NoError; } /// - /// Returns the next Huffman-coded value from the bit-stream, decoded according to the given value. + /// Extracts the next Huffman-coded value from the bit-stream into result, decoded according to the given value. /// /// The huffman value - /// The - public byte DecodeHuffman(ref HuffmanTree huffmanTree) + /// The decoded + /// The + public DecoderErrorCode DecodeHuffmanUnsafe(ref HuffmanTree huffmanTree, out byte result) { + result = 0; + // Copy stuff to the stack: if (huffmanTree.Length == 0) { @@ -558,16 +568,19 @@ namespace ImageSharp.Formats int n = (v & 0xFF) - 1; this.Bits.UnreadBits -= n; this.Bits.Mask >>= n; - return (byte)(v >> 8); + result = (byte)(v >> 8); + return errorCode; } } - else if (errorCode == DecoderErrorCode.UnexpectedEndOfStream) - { - errorCode.ThrowExceptionForErrorCode(); - } + + // else if (errorCode == DecoderErrorCode.UnexpectedEndOfStream) + // { + // errorCode.ThrowExceptionForErrorCode(); + // } else { this.UnreadByteStuffedByte(); + return errorCode; } } @@ -589,13 +602,18 @@ namespace ImageSharp.Formats if (code <= huffmanTree.MaxCodes[i]) { - return huffmanTree.Values[huffmanTree.Indices[i] + code - huffmanTree.MinCodes[i]]; + result = huffmanTree.Values[huffmanTree.Indices[i] + code - huffmanTree.MinCodes[i]]; + return DecoderErrorCode.NoError; } code <<= 1; } - throw new ImageFormatException("Bad Huffman code"); + // Unrecoverable error, throwing: + DecoderThrowHelper.ThrowImageFormatException.BadHuffmanCode(); + + // DUMMY RETURN! C# doesn't know we have thrown an exception! + return DecoderErrorCode.NoError; } ///