Browse Source

DecodeHuffmanUnsafe, DecodeBitUnsafe

af/merge-core
Anton Firszov 9 years ago
parent
commit
4096ee8d12
  1. 29
      src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs
  2. 2
      src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs
  3. 26
      src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs
  4. 57
      src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs
  5. 62
      src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs

29
src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs

@ -94,24 +94,45 @@ namespace ImageSharp.Formats.Jpg
/// <param name="t">Byte</param> /// <param name="t">Byte</param>
/// <param name="decoder">Jpeg decoder</param> /// <param name="decoder">Jpeg decoder</param>
/// <returns>Read bits value</returns> /// <returns>Read bits value</returns>
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;
}
/// <summary>
/// Receive extend
/// </summary>
/// <param name="t">Byte</param>
/// <param name="decoder">Jpeg decoder</param>
/// <param name="x">Read bits value</param>
/// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode ReceiveExtendUnsafe(byte t, JpegDecoderCore decoder, out int x)
{ {
if (this.UnreadBits < t) 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.UnreadBits -= t;
this.Mask >>= t; this.Mask >>= t;
int s = 1 << 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)) if (x < (s >> 1))
{ {
x += ((-1) << t) + 1; x += ((-1) << t) + 1;
} }
return x; return DecoderErrorCode.NoError;
} }
} }
} }

2
src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs

@ -101,6 +101,7 @@ namespace ImageSharp.Formats.Jpg
{ {
return errorCode; return errorCode;
} }
if (x != JpegConstants.Markers.XFF) if (x != JpegConstants.Markers.XFF)
{ {
return DecoderErrorCode.NoError; return DecoderErrorCode.NoError;
@ -112,6 +113,7 @@ namespace ImageSharp.Formats.Jpg
{ {
return errorCode; return errorCode;
} }
if (x != 0x00) if (x != 0x00)
{ {
return DecoderErrorCode.MissingFF00; return DecoderErrorCode.MissingFF00;

26
src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs

@ -46,18 +46,40 @@ namespace ImageSharp.Formats.Jpg
} }
} }
/// <summary>
/// Throws an exception if the given <see cref="DecoderErrorCode"/> is <see cref="DecoderErrorCode.UnexpectedEndOfStream"/>.
/// </summary>
/// <param name="errorCode">The <see cref="DecoderErrorCode"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void EnsureNoEOF(this DecoderErrorCode errorCode)
{
if (errorCode == DecoderErrorCode.UnexpectedEndOfStream)
{
errorCode.ThrowExceptionForErrorCode();
}
}
/// <summary> /// <summary>
/// Encapsulates methods throwing different flavours of <see cref="ImageFormatException"/>-s. /// Encapsulates methods throwing different flavours of <see cref="ImageFormatException"/>-s.
/// </summary> /// </summary>
public static class ThrowImageFormatException public static class ThrowImageFormatException
{ {
/// <summary> /// <summary>
/// Throws "Fill called when unread bytes exist." /// Throws "Fill called when unread bytes exist".
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static void FillCalledWhenUnreadBytesExist() public static void FillCalledWhenUnreadBytesExist()
{ {
throw new ImageFormatException("Fill called when unread bytes exist."); throw new ImageFormatException("Fill called when unread bytes exist!");
}
/// <summary>
/// Throws "Bad Huffman code".
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)]
public static void BadHuffmanCode()
{
throw new ImageFormatException("Bad Huffman code!");
} }
} }
} }

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

@ -308,7 +308,7 @@ namespace ImageSharp.Formats.Jpg
private void ProcessBlockImpl(JpegDecoderCore decoder, int scanIndex) private void ProcessBlockImpl(JpegDecoderCore decoder, int scanIndex)
{ {
var b = this.pointers.Block; var b = this.pointers.Block;
DecoderErrorCode errorCode;
int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector;
if (this.ah != 0) if (this.ah != 0)
{ {
@ -322,9 +322,13 @@ namespace ImageSharp.Formats.Jpg
zig++; zig++;
// Decode the DC coefficient, as specified in section F.2.2.1. // Decode the DC coefficient, as specified in section F.2.2.1.
byte value = byte value;
decoder.DecodeHuffman( int huffmanIndex = (DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector;
ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector]); errorCode = decoder.DecodeHuffmanUnsafe(
ref decoder.HuffmanTrees[huffmanIndex],
out value);
errorCode.EnsureNoEOF();
if (value > 16) if (value > 16)
{ {
throw new ImageFormatException("Excessive DC component"); 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. // Decode the AC coefficients, as specified in section F.2.2.2.
for (; zig <= this.zigEnd; zig++) 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 val0 = (byte)(value >> 4);
byte val1 = (byte)(value & 0x0f); byte val1 = (byte)(value & 0x0f);
if (val1 != 0) if (val1 != 0)
@ -370,7 +377,8 @@ namespace ImageSharp.Formats.Jpg
this.eobRun = (ushort)(1 << val0); this.eobRun = (ushort)(1 << val0);
if (val0 != 0) if (val0 != 0)
{ {
this.eobRun |= (ushort)decoder.DecodeBits(val0); errorCode = this.DecodeEobRun(val0, decoder);
errorCode.EnsureNoError();
} }
this.eobRun--; this.eobRun--;
@ -409,6 +417,19 @@ namespace ImageSharp.Formats.Jpg
destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2); 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;
}
/// <summary> /// <summary>
/// Gets the block index used to retieve blocks from in <see cref="JpegDecoderCore.DecodedBlocks"/> /// Gets the block index used to retieve blocks from in <see cref="JpegDecoderCore.DecodedBlocks"/>
/// </summary> /// </summary>
@ -496,7 +517,9 @@ namespace ImageSharp.Formats.Jpg
throw new ImageFormatException("Invalid state for zig DC component"); 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) if (bit)
{ {
int stuff = (int)Block8x8F.GetScalarAt(b, 0); int stuff = (int)Block8x8F.GetScalarAt(b, 0);
@ -519,7 +542,11 @@ namespace ImageSharp.Formats.Jpg
{ {
bool done = false; bool done = false;
int z = 0; 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 val0 = val >> 4;
int val1 = val & 0x0f; int val1 = val & 0x0f;
@ -531,7 +558,8 @@ namespace ImageSharp.Formats.Jpg
this.eobRun = (ushort)(1 << val0); this.eobRun = (ushort)(1 << val0);
if (val0 != 0) if (val0 != 0)
{ {
this.eobRun |= (ushort)decoder.DecodeBits(val0); errorCode = this.DecodeEobRun(val0, decoder);
errorCode.EnsureNoError();
} }
done = true; done = true;
@ -540,7 +568,11 @@ namespace ImageSharp.Formats.Jpg
break; break;
case 1: case 1:
z = delta; z = delta;
bool bit = decoder.DecodeBit();
bool bit;
errorCode = decoder.DecodeBitUnsafe(out bit);
errorCode.EnsureNoError();
if (!bit) if (!bit)
{ {
z = -z; z = -z;
@ -606,7 +638,10 @@ namespace ImageSharp.Formats.Jpg
continue; continue;
} }
bool bit = decoder.DecodeBit(); bool bit;
DecoderErrorCode errorCode = decoder.DecodeBitUnsafe(out bit);
errorCode.EnsureNoError();
if (!bit) if (!bit)
{ {
continue; continue;

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

@ -435,25 +435,31 @@ namespace ImageSharp.Formats
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte ReadByte() public byte ReadByte()
{ {
byte result = this.Bytes.ReadByte(this.InputStream); return this.Bytes.ReadByte(this.InputStream);
return result;
} }
/// <summary> /// <summary>
/// Decodes a single bit /// Decodes a single bit
/// TODO: This method (and also the usages) could be optimized by batching!
/// </summary> /// </summary>
/// <returns>The <see cref="bool" /></returns> /// <param name="result">The decoded bit as a <see cref="bool"/></param>
public bool DecodeBit() /// <returns>The <see cref="DecoderErrorCode" /></returns>
public DecoderErrorCode DecodeBitUnsafe(out bool result)
{ {
if (this.Bits.UnreadBits == 0) 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.UnreadBits--;
this.Bits.Mask >>= 1; this.Bits.Mask >>= 1;
return ret; return DecoderErrorCode.NoError;
} }
/// <summary> /// <summary>
@ -517,28 +523,32 @@ namespace ImageSharp.Formats
/// Decodes the given number of bits /// Decodes the given number of bits
/// </summary> /// </summary>
/// <param name="count">The number of bits to decode.</param> /// <param name="count">The number of bits to decode.</param>
/// <returns>The <see cref="uint" /></returns> /// <param name="result">The <see cref="uint" /> result</param>
public uint DecodeBits(int count) /// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode DecodeBitsUnsafe(int count, out uint result)
{ {
if (this.Bits.UnreadBits < count) if (this.Bits.UnreadBits < count)
{ {
this.Bits.EnsureNBits(count, this); this.Bits.EnsureNBits(count, this);
} }
uint ret = this.Bits.Accumulator >> (this.Bits.UnreadBits - count); result = this.Bits.Accumulator >> (this.Bits.UnreadBits - count);
ret = (uint)(ret & ((1 << count) - 1)); result = (uint)(result & ((1 << count) - 1));
this.Bits.UnreadBits -= count; this.Bits.UnreadBits -= count;
this.Bits.Mask >>= count; this.Bits.Mask >>= count;
return ret; return DecoderErrorCode.NoError;
} }
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
/// <param name="huffmanTree">The huffman value</param> /// <param name="huffmanTree">The huffman value</param>
/// <returns>The <see cref="byte" /></returns> /// <param name="result">The decoded <see cref="byte" /></param>
public byte DecodeHuffman(ref HuffmanTree huffmanTree) /// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode DecodeHuffmanUnsafe(ref HuffmanTree huffmanTree, out byte result)
{ {
result = 0;
// Copy stuff to the stack: // Copy stuff to the stack:
if (huffmanTree.Length == 0) if (huffmanTree.Length == 0)
{ {
@ -558,16 +568,19 @@ namespace ImageSharp.Formats
int n = (v & 0xFF) - 1; int n = (v & 0xFF) - 1;
this.Bits.UnreadBits -= n; this.Bits.UnreadBits -= n;
this.Bits.Mask >>= n; this.Bits.Mask >>= n;
return (byte)(v >> 8); result = (byte)(v >> 8);
return errorCode;
} }
} }
else if (errorCode == DecoderErrorCode.UnexpectedEndOfStream)
{ // else if (errorCode == DecoderErrorCode.UnexpectedEndOfStream)
errorCode.ThrowExceptionForErrorCode(); // {
} // errorCode.ThrowExceptionForErrorCode();
// }
else else
{ {
this.UnreadByteStuffedByte(); this.UnreadByteStuffedByte();
return errorCode;
} }
} }
@ -589,13 +602,18 @@ namespace ImageSharp.Formats
if (code <= huffmanTree.MaxCodes[i]) 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; 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;
} }
/// <summary> /// <summary>

Loading…
Cancel
Save