From 12bfd34c18b5c0b776859bf0494d05daa0995f9d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 23 Jan 2017 04:31:32 +0100 Subject: [PATCH] use int-s in Huffman decoder when possible --- .../Components/Decoder/Bits.cs | 10 +-- .../Components/Decoder/Bytes.cs | 63 ++++++++++++++++--- .../Components/Decoder/DecoderThrowHelper.cs | 9 +++ .../Components/Decoder/HuffmanTree.cs | 55 ++++++++++------ .../Components/Decoder/JpegScanDecoder.cs | 18 +++--- src/ImageSharp.Formats.Jpeg/JpegConstants.cs | 5 ++ .../JpegDecoderCore.cs | 17 ++--- 7 files changed, 126 insertions(+), 51 deletions(-) diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs index 1eea1417f3..80787101b0 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs @@ -18,13 +18,13 @@ namespace ImageSharp.Formats.Jpg /// /// Gets or sets the accumulator. /// - public uint Accumulator; + public int Accumulator; /// /// Gets or sets the mask. /// 0, with mask==0 when unreadbits==0.]]> /// - public uint Mask; + public int Mask; /// /// Gets or sets the number of unread bits in the accumulator. @@ -89,7 +89,7 @@ namespace ImageSharp.Formats.Jpg private DecoderErrorCode EnsureBitsStepImpl(JpegDecoderCore decoder) { - byte c; + int c; DecoderErrorCode errorCode = decoder.Bytes.ReadByteStuffedByteUnsafe(decoder.InputStream, out c); if (errorCode != DecoderErrorCode.NoError) @@ -117,7 +117,7 @@ namespace ImageSharp.Formats.Jpg /// Jpeg decoder /// Read bits value [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int ReceiveExtend(byte t, JpegDecoderCore decoder) + public int ReceiveExtend(int t, JpegDecoderCore decoder) { int x; DecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, decoder, out x); @@ -132,7 +132,7 @@ namespace ImageSharp.Formats.Jpg /// Jpeg decoder /// Read bits value /// The - public DecoderErrorCode ReceiveExtendUnsafe(byte t, JpegDecoderCore decoder, out int x) + public DecoderErrorCode ReceiveExtendUnsafe(int t, JpegDecoderCore decoder, out int x) { if (this.UnreadBits < t) { diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs index 27f902aa15..b65df51dc4 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs @@ -16,6 +16,11 @@ namespace ImageSharp.Formats.Jpg /// internal struct Bytes : IDisposable { + /// + /// Specifies the buffer size for and + /// + public const int BufferSize = 4096; + /// /// Gets or sets the buffer. /// buffer[i:j] are the buffered bytes read from the underlying @@ -23,6 +28,11 @@ namespace ImageSharp.Formats.Jpg /// public byte[] Buffer; + /// + /// Values of converted to -s + /// + public int[] BufferAsInt; + /// /// Start of bytes read /// @@ -39,7 +49,9 @@ namespace ImageSharp.Formats.Jpg /// public int UnreadableBytes; - private static readonly ArrayPool ArrayPool = ArrayPool.Create(4096, 50); + private static readonly ArrayPool BytePool = ArrayPool.Create(BufferSize, 50); + + private static readonly ArrayPool IntPool = ArrayPool.Create(BufferSize, 50); /// /// Creates a new instance of the , and initializes it's buffer. @@ -47,7 +59,11 @@ namespace ImageSharp.Formats.Jpg /// The bytes created public static Bytes Create() { - return new Bytes { Buffer = ArrayPool.Rent(4096) }; + return new Bytes + { + Buffer = BytePool.Rent(BufferSize), + BufferAsInt = IntPool.Rent(BufferSize) + }; } /// @@ -57,32 +73,34 @@ namespace ImageSharp.Formats.Jpg { if (this.Buffer != null) { - ArrayPool.Return(this.Buffer); + BytePool.Return(this.Buffer); + IntPool.Return(this.BufferAsInt); } this.Buffer = null; + this.BufferAsInt = null; } /// /// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data. /// /// Input stream - /// The result + /// The result byte as /// The - public DecoderErrorCode ReadByteStuffedByteUnsafe(Stream inputStream, out byte x) + public DecoderErrorCode ReadByteStuffedByteUnsafe(Stream inputStream, out int x) { // Take the fast path if bytes.buf contains at least two bytes. if (this.I + 2 <= this.J) { - x = this.Buffer[this.I]; + x = this.BufferAsInt[this.I]; this.I++; this.UnreadableBytes = 1; - if (x != JpegConstants.Markers.XFF) + if (x != JpegConstants.Markers.XFFInt) { return DecoderErrorCode.NoError; } - if (this.Buffer[this.I] != 0x00) + if (this.BufferAsInt[this.I] != 0x00) { return DecoderErrorCode.MissingFF00; } @@ -95,7 +113,7 @@ namespace ImageSharp.Formats.Jpg this.UnreadableBytes = 0; - DecoderErrorCode errorCode = this.ReadByteUnsafe(inputStream, out x); + DecoderErrorCode errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); this.UnreadableBytes = 1; if (errorCode != DecoderErrorCode.NoError) { @@ -107,7 +125,7 @@ namespace ImageSharp.Formats.Jpg return DecoderErrorCode.NoError; } - errorCode = this.ReadByteUnsafe(inputStream, out x); + errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); this.UnreadableBytes = 2; if (errorCode != DecoderErrorCode.NoError) { @@ -164,6 +182,25 @@ namespace ImageSharp.Formats.Jpg return errorCode; } + public DecoderErrorCode ReadByteAsIntUnsafe(Stream inputStream, out int result) + { + DecoderErrorCode errorCode = DecoderErrorCode.NoError; + while (this.I == this.J) + { + errorCode = this.FillUnsafe(inputStream); + if (errorCode != DecoderErrorCode.NoError) + { + result = 0; + return errorCode; + } + } + + result = this.BufferAsInt[this.I]; + this.I++; + this.UnreadableBytes = 0; + return errorCode; + } + /// /// Fills up the bytes buffer from the underlying stream. /// It should only be called when there are no unread bytes in bytes. @@ -211,6 +248,12 @@ namespace ImageSharp.Formats.Jpg } this.J += n; + + for (int i = 0; i < this.Buffer.Length; i++) + { + this.BufferAsInt[i] = this.Buffer[i]; + } + return DecoderErrorCode.NoError; } } diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs index 84f7579c8a..9ce5ea4146 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs @@ -81,6 +81,15 @@ namespace ImageSharp.Formats.Jpg { throw new ImageFormatException("Bad Huffman code!"); } + + /// + /// Throws "Uninitialized Huffman table". + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static void UninitializedHuffmanTable() + { + throw new ImageFormatException("Uninitialized Huffman table"); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs index e06d644a7f..55cf8c77f6 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs @@ -45,7 +45,7 @@ namespace ImageSharp.Formats.Jpg /// /// The log-2 size of the Huffman decoder's look-up table. /// - public const int LutSize = 8; + public const int LutSizeLog2 = 8; /// /// Gets or sets the number of codes in the tree. @@ -58,13 +58,18 @@ namespace ImageSharp.Formats.Jpg /// are 1 plus the code length, or 0 if the value is too large to fit in /// lutSize bits. /// - public ushort[] Lut; + public int[] Lut; /// /// Gets the the decoded values, sorted by their encoding. /// public byte[] Values; + /// + /// Same as , converted to int-s + /// + public int[] ValuesAsInt; + /// /// Gets the array of minimum codes. /// MinCodes[i] is the minimum code of length i, or -1 if there are no codes of that length. @@ -82,11 +87,11 @@ namespace ImageSharp.Formats.Jpg /// public int[] Indices; - private static readonly ArrayPool UshortBuffer = ArrayPool.Create(1 << LutSize, 50); + private static readonly ArrayPool IntBuffer256 = ArrayPool.Create(MaxNCodes, 50); - private static readonly ArrayPool ByteBuffer = ArrayPool.Create(MaxNCodes, 50); + private static readonly ArrayPool ByteBuffer256 = ArrayPool.Create(MaxNCodes, 50); - private static readonly ArrayPool IntBuffer = ArrayPool.Create(MaxCodeLength, 50); + private static readonly ArrayPool CodesBuffer16 = ArrayPool.Create(MaxCodeLength, 50); /// /// Creates and initializes an array of instances of size @@ -111,11 +116,12 @@ namespace ImageSharp.Formats.Jpg /// public void Dispose() { - UshortBuffer.Return(this.Lut, true); - ByteBuffer.Return(this.Values, true); - IntBuffer.Return(this.MinCodes, true); - IntBuffer.Return(this.MaxCodes, true); - IntBuffer.Return(this.Indices, true); + IntBuffer256.Return(this.Lut, true); + IntBuffer256.Return(this.ValuesAsInt, true); + ByteBuffer256.Return(this.Values, true); + CodesBuffer16.Return(this.MinCodes, true); + CodesBuffer16.Return(this.MaxCodes, true); + CodesBuffer16.Return(this.Indices, true); } /// @@ -159,15 +165,20 @@ namespace ImageSharp.Formats.Jpg decoder.ReadFull(this.Values, 0, this.Length); + for (int i = 0; i < this.Values.Length; i++) + { + this.ValuesAsInt[i] = this.Values[i]; + } + // Derive the look-up table. for (int i = 0; i < this.Lut.Length; i++) { this.Lut[i] = 0; } - uint x = 0, code = 0; + int x = 0, code = 0; - for (int i = 0; i < LutSize; i++) + for (int i = 0; i < LutSizeLog2; i++) { code <<= 1; @@ -178,8 +189,8 @@ namespace ImageSharp.Formats.Jpg // whose codeLength's high bits matches code. // The high 8 bits of lutValue are the encoded value. // The low 8 bits are 1 plus the codeLength. - byte base2 = (byte)(code << (7 - i)); - ushort lutValue = (ushort)((this.Values[x] << 8) | (2 + i)); + int base2 = (code << (7 - i)); + int lutValue = (this.ValuesAsInt[x] << 8) | (2 + i); for (int k = 0; k < 1 << (7 - i); k++) { @@ -215,16 +226,22 @@ namespace ImageSharp.Formats.Jpg } } + public int GetValue(int code, int i) + { + return this.ValuesAsInt[this.Indices[i] + code - this.MinCodes[i]]; + } + /// /// Initializes the Huffman tree /// private void Init() { - this.Lut = UshortBuffer.Rent(1 << LutSize); - this.Values = ByteBuffer.Rent(MaxNCodes); - this.MinCodes = IntBuffer.Rent(MaxCodeLength); - this.MaxCodes = IntBuffer.Rent(MaxCodeLength); - this.Indices = IntBuffer.Rent(MaxCodeLength); + this.Lut = IntBuffer256.Rent(MaxNCodes); + this.Values = ByteBuffer256.Rent(MaxNCodes); + this.ValuesAsInt = IntBuffer256.Rent(MaxNCodes); + this.MinCodes = CodesBuffer16.Rent(MaxCodeLength); + this.MaxCodes = CodesBuffer16.Rent(MaxCodeLength); + this.Indices = CodesBuffer16.Rent(MaxCodeLength); } } } \ 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 4dcf6def8f..86c558af7b 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs @@ -85,7 +85,7 @@ namespace ImageSharp.Formats.Jpg /// /// End-of-Band run, specified in section G.1.2.2. /// - private ushort eobRun; + private int eobRun; /// /// Pointers to elements of @@ -349,7 +349,7 @@ namespace ImageSharp.Formats.Jpg zig++; // Decode the DC coefficient, as specified in section F.2.2.1. - byte value; + int value; int huffmanIndex = (DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; errorCode = decoder.DecodeHuffmanUnsafe( ref decoder.HuffmanTrees[huffmanIndex], @@ -378,12 +378,12 @@ namespace ImageSharp.Formats.Jpg // Decode the AC coefficients, as specified in section F.2.2.2. for (; zig <= this.zigEnd; zig++) { - byte value; + int value; errorCode = decoder.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value); errorCode.EnsureNoEOF(); - byte val0 = (byte)(value >> 4); - byte val1 = (byte)(value & 0x0f); + int val0 = value >> 4; + int val1 = value & 0x0f; if (val1 != 0) { zig += val0; @@ -424,14 +424,14 @@ namespace ImageSharp.Formats.Jpg private DecoderErrorCode DecodeEobRun(int count, JpegDecoderCore decoder) { - uint bitsResult; + int bitsResult; DecoderErrorCode errorCode = decoder.DecodeBitsUnsafe(count, out bitsResult); if (errorCode != DecoderErrorCode.NoError) { return errorCode; } - this.eobRun |= (ushort)bitsResult; + this.eobRun |= bitsResult; return DecoderErrorCode.NoError; } @@ -548,7 +548,7 @@ namespace ImageSharp.Formats.Jpg bool done = false; int z = 0; - byte val; + int val; DecoderErrorCode errorCode = decoder.DecodeHuffmanUnsafe(ref h, out val); errorCode.EnsureNoEOF(); @@ -560,7 +560,7 @@ namespace ImageSharp.Formats.Jpg case 0: if (val0 != 0x0f) { - this.eobRun = (ushort)(1 << val0); + this.eobRun = 1 << val0; if (val0 != 0) { errorCode = this.DecodeEobRun(val0, decoder); diff --git a/src/ImageSharp.Formats.Jpeg/JpegConstants.cs b/src/ImageSharp.Formats.Jpeg/JpegConstants.cs index 19d726e708..74f9a3c07d 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegConstants.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegConstants.cs @@ -86,6 +86,11 @@ namespace ImageSharp.Formats /// public const byte XFF = 0xff; + /// + /// Same as but of type + /// + public const int XFFInt = XFF; + /// /// Start of Image /// diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs index 1a039191e4..64f19a3a29 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs @@ -307,7 +307,7 @@ namespace ImageSharp.Formats /// The number of bits to decode. /// The result /// The - public DecoderErrorCode DecodeBitsUnsafe(int count, out uint result) + public DecoderErrorCode DecodeBitsUnsafe(int count, out int result) { if (this.Bits.UnreadBits < count) { @@ -315,7 +315,7 @@ namespace ImageSharp.Formats } result = this.Bits.Accumulator >> (this.Bits.UnreadBits - count); - result = (uint)(result & ((1 << count) - 1)); + result = result & ((1 << count) - 1); this.Bits.UnreadBits -= count; this.Bits.Mask >>= count; return DecoderErrorCode.NoError; @@ -327,14 +327,13 @@ namespace ImageSharp.Formats /// The huffman value /// The decoded /// The - public DecoderErrorCode DecodeHuffmanUnsafe(ref HuffmanTree huffmanTree, out byte result) + public DecoderErrorCode DecodeHuffmanUnsafe(ref HuffmanTree huffmanTree, out int result) { result = 0; - // Copy stuff to the stack: if (huffmanTree.Length == 0) { - throw new ImageFormatException("Uninitialized Huffman table"); + DecoderThrowHelper.ThrowImageFormatException.UninitializedHuffmanTable(); } if (this.Bits.UnreadBits < 8) @@ -344,14 +343,15 @@ namespace ImageSharp.Formats if (errorCode == DecoderErrorCode.NoError) { - ushort v = huffmanTree.Lut[(this.Bits.Accumulator >> (this.Bits.UnreadBits - HuffmanTree.LutSize)) & 0xFF]; + int lutIndex = (this.Bits.Accumulator >> (this.Bits.UnreadBits - HuffmanTree.LutSizeLog2)) & 0xFF; + int v = huffmanTree.Lut[lutIndex]; if (v != 0) { int n = (v & 0xFF) - 1; this.Bits.UnreadBits -= n; this.Bits.Mask >>= n; - result = (byte)(v >> 8); + result = v >> 8; return errorCode; } } @@ -380,7 +380,8 @@ namespace ImageSharp.Formats if (code <= huffmanTree.MaxCodes[i]) { - result = huffmanTree.Values[huffmanTree.Indices[i] + code - huffmanTree.MinCodes[i]]; + // ValuesAsInt[huffmanTree.Indices[i] + code - huffmanTree.MinCodes[i]]; + result = huffmanTree.GetValue(code, i); return DecoderErrorCode.NoError; }