Browse Source

use int-s in Huffman decoder when possible

pull/90/head
Anton Firszov 9 years ago
parent
commit
12bfd34c18
  1. 10
      src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs
  2. 63
      src/ImageSharp.Formats.Jpeg/Components/Decoder/Bytes.cs
  3. 9
      src/ImageSharp.Formats.Jpeg/Components/Decoder/DecoderThrowHelper.cs
  4. 55
      src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs
  5. 18
      src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs
  6. 5
      src/ImageSharp.Formats.Jpeg/JpegConstants.cs
  7. 17
      src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs

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

@ -18,13 +18,13 @@ namespace ImageSharp.Formats.Jpg
/// <summary> /// <summary>
/// Gets or sets the accumulator. /// Gets or sets the accumulator.
/// </summary> /// </summary>
public uint Accumulator; public int Accumulator;
/// <summary> /// <summary>
/// Gets or sets the mask. /// Gets or sets the mask.
/// <![CDATA[mask==1<<(unreadbits-1) when unreadbits>0, with mask==0 when unreadbits==0.]]> /// <![CDATA[mask==1<<(unreadbits-1) when unreadbits>0, with mask==0 when unreadbits==0.]]>
/// </summary> /// </summary>
public uint Mask; public int Mask;
/// <summary> /// <summary>
/// Gets or sets the number of unread bits in the accumulator. /// Gets or sets the number of unread bits in the accumulator.
@ -89,7 +89,7 @@ namespace ImageSharp.Formats.Jpg
private DecoderErrorCode EnsureBitsStepImpl(JpegDecoderCore decoder) private DecoderErrorCode EnsureBitsStepImpl(JpegDecoderCore decoder)
{ {
byte c; int c;
DecoderErrorCode errorCode = decoder.Bytes.ReadByteStuffedByteUnsafe(decoder.InputStream, out c); DecoderErrorCode errorCode = decoder.Bytes.ReadByteStuffedByteUnsafe(decoder.InputStream, out c);
if (errorCode != DecoderErrorCode.NoError) if (errorCode != DecoderErrorCode.NoError)
@ -117,7 +117,7 @@ namespace ImageSharp.Formats.Jpg
/// <param name="decoder">Jpeg decoder</param> /// <param name="decoder">Jpeg decoder</param>
/// <returns>Read bits value</returns> /// <returns>Read bits value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReceiveExtend(byte t, JpegDecoderCore decoder) public int ReceiveExtend(int t, JpegDecoderCore decoder)
{ {
int x; int x;
DecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, decoder, out x); DecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, decoder, out x);
@ -132,7 +132,7 @@ namespace ImageSharp.Formats.Jpg
/// <param name="decoder">Jpeg decoder</param> /// <param name="decoder">Jpeg decoder</param>
/// <param name="x">Read bits value</param> /// <param name="x">Read bits value</param>
/// <returns>The <see cref="DecoderErrorCode"/></returns> /// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode ReceiveExtendUnsafe(byte t, JpegDecoderCore decoder, out int x) public DecoderErrorCode ReceiveExtendUnsafe(int t, JpegDecoderCore decoder, out int x)
{ {
if (this.UnreadBits < t) if (this.UnreadBits < t)
{ {

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

@ -16,6 +16,11 @@ namespace ImageSharp.Formats.Jpg
/// </summary> /// </summary>
internal struct Bytes : IDisposable internal struct Bytes : IDisposable
{ {
/// <summary>
/// Specifies the buffer size for <see cref="Buffer"/> and <see cref="BufferAsInt"/>
/// </summary>
public const int BufferSize = 4096;
/// <summary> /// <summary>
/// Gets or sets the buffer. /// Gets or sets the buffer.
/// buffer[i:j] are the buffered bytes read from the underlying /// buffer[i:j] are the buffered bytes read from the underlying
@ -23,6 +28,11 @@ namespace ImageSharp.Formats.Jpg
/// </summary> /// </summary>
public byte[] Buffer; public byte[] Buffer;
/// <summary>
/// Values of <see cref="Buffer"/> converted to <see cref="int"/>-s
/// </summary>
public int[] BufferAsInt;
/// <summary> /// <summary>
/// Start of bytes read /// Start of bytes read
/// </summary> /// </summary>
@ -39,7 +49,9 @@ namespace ImageSharp.Formats.Jpg
/// </summary> /// </summary>
public int UnreadableBytes; public int UnreadableBytes;
private static readonly ArrayPool<byte> ArrayPool = ArrayPool<byte>.Create(4096, 50); private static readonly ArrayPool<byte> BytePool = ArrayPool<byte>.Create(BufferSize, 50);
private static readonly ArrayPool<int> IntPool = ArrayPool<int>.Create(BufferSize, 50);
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="Bytes"/>, and initializes it's buffer. /// Creates a new instance of the <see cref="Bytes"/>, and initializes it's buffer.
@ -47,7 +59,11 @@ namespace ImageSharp.Formats.Jpg
/// <returns>The bytes created</returns> /// <returns>The bytes created</returns>
public static Bytes Create() public static Bytes Create()
{ {
return new Bytes { Buffer = ArrayPool.Rent(4096) }; return new Bytes
{
Buffer = BytePool.Rent(BufferSize),
BufferAsInt = IntPool.Rent(BufferSize)
};
} }
/// <summary> /// <summary>
@ -57,32 +73,34 @@ namespace ImageSharp.Formats.Jpg
{ {
if (this.Buffer != null) if (this.Buffer != null)
{ {
ArrayPool.Return(this.Buffer); BytePool.Return(this.Buffer);
IntPool.Return(this.BufferAsInt);
} }
this.Buffer = null; this.Buffer = null;
this.BufferAsInt = null;
} }
/// <summary> /// <summary>
/// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data. /// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data.
/// </summary> /// </summary>
/// <param name="inputStream">Input stream</param> /// <param name="inputStream">Input stream</param>
/// <param name="x">The result <see cref="byte"/></param> /// <param name="x">The result byte as <see cref="int"/></param>
/// <returns>The <see cref="DecoderErrorCode"/></returns> /// <returns>The <see cref="DecoderErrorCode"/></returns>
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. // Take the fast path if bytes.buf contains at least two bytes.
if (this.I + 2 <= this.J) if (this.I + 2 <= this.J)
{ {
x = this.Buffer[this.I]; x = this.BufferAsInt[this.I];
this.I++; this.I++;
this.UnreadableBytes = 1; this.UnreadableBytes = 1;
if (x != JpegConstants.Markers.XFF) if (x != JpegConstants.Markers.XFFInt)
{ {
return DecoderErrorCode.NoError; return DecoderErrorCode.NoError;
} }
if (this.Buffer[this.I] != 0x00) if (this.BufferAsInt[this.I] != 0x00)
{ {
return DecoderErrorCode.MissingFF00; return DecoderErrorCode.MissingFF00;
} }
@ -95,7 +113,7 @@ namespace ImageSharp.Formats.Jpg
this.UnreadableBytes = 0; this.UnreadableBytes = 0;
DecoderErrorCode errorCode = this.ReadByteUnsafe(inputStream, out x); DecoderErrorCode errorCode = this.ReadByteAsIntUnsafe(inputStream, out x);
this.UnreadableBytes = 1; this.UnreadableBytes = 1;
if (errorCode != DecoderErrorCode.NoError) if (errorCode != DecoderErrorCode.NoError)
{ {
@ -107,7 +125,7 @@ namespace ImageSharp.Formats.Jpg
return DecoderErrorCode.NoError; return DecoderErrorCode.NoError;
} }
errorCode = this.ReadByteUnsafe(inputStream, out x); errorCode = this.ReadByteAsIntUnsafe(inputStream, out x);
this.UnreadableBytes = 2; this.UnreadableBytes = 2;
if (errorCode != DecoderErrorCode.NoError) if (errorCode != DecoderErrorCode.NoError)
{ {
@ -164,6 +182,25 @@ namespace ImageSharp.Formats.Jpg
return errorCode; 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;
}
/// <summary> /// <summary>
/// Fills up the bytes buffer from the underlying stream. /// Fills up the bytes buffer from the underlying stream.
/// It should only be called when there are no unread bytes in bytes. /// It should only be called when there are no unread bytes in bytes.
@ -211,6 +248,12 @@ namespace ImageSharp.Formats.Jpg
} }
this.J += n; this.J += n;
for (int i = 0; i < this.Buffer.Length; i++)
{
this.BufferAsInt[i] = this.Buffer[i];
}
return DecoderErrorCode.NoError; return DecoderErrorCode.NoError;
} }
} }

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

@ -81,6 +81,15 @@ namespace ImageSharp.Formats.Jpg
{ {
throw new ImageFormatException("Bad Huffman code!"); throw new ImageFormatException("Bad Huffman code!");
} }
/// <summary>
/// Throws "Uninitialized Huffman table".
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)]
public static void UninitializedHuffmanTable()
{
throw new ImageFormatException("Uninitialized Huffman table");
}
} }
} }
} }

55
src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs

@ -45,7 +45,7 @@ namespace ImageSharp.Formats.Jpg
/// <summary> /// <summary>
/// The log-2 size of the Huffman decoder's look-up table. /// The log-2 size of the Huffman decoder's look-up table.
/// </summary> /// </summary>
public const int LutSize = 8; public const int LutSizeLog2 = 8;
/// <summary> /// <summary>
/// Gets or sets the number of codes in the tree. /// 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 /// are 1 plus the code length, or 0 if the value is too large to fit in
/// lutSize bits. /// lutSize bits.
/// </summary> /// </summary>
public ushort[] Lut; public int[] Lut;
/// <summary> /// <summary>
/// Gets the the decoded values, sorted by their encoding. /// Gets the the decoded values, sorted by their encoding.
/// </summary> /// </summary>
public byte[] Values; public byte[] Values;
/// <summary>
/// Same as <see cref="Values"/>, converted to int-s
/// </summary>
public int[] ValuesAsInt;
/// <summary> /// <summary>
/// Gets the array of minimum codes. /// 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. /// 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
/// </summary> /// </summary>
public int[] Indices; public int[] Indices;
private static readonly ArrayPool<ushort> UshortBuffer = ArrayPool<ushort>.Create(1 << LutSize, 50); private static readonly ArrayPool<int> IntBuffer256 = ArrayPool<int>.Create(MaxNCodes, 50);
private static readonly ArrayPool<byte> ByteBuffer = ArrayPool<byte>.Create(MaxNCodes, 50); private static readonly ArrayPool<byte> ByteBuffer256 = ArrayPool<byte>.Create(MaxNCodes, 50);
private static readonly ArrayPool<int> IntBuffer = ArrayPool<int>.Create(MaxCodeLength, 50); private static readonly ArrayPool<int> CodesBuffer16 = ArrayPool<int>.Create(MaxCodeLength, 50);
/// <summary> /// <summary>
/// Creates and initializes an array of <see cref="HuffmanTree" /> instances of size <see cref="NumberOfTrees" /> /// Creates and initializes an array of <see cref="HuffmanTree" /> instances of size <see cref="NumberOfTrees" />
@ -111,11 +116,12 @@ namespace ImageSharp.Formats.Jpg
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
UshortBuffer.Return(this.Lut, true); IntBuffer256.Return(this.Lut, true);
ByteBuffer.Return(this.Values, true); IntBuffer256.Return(this.ValuesAsInt, true);
IntBuffer.Return(this.MinCodes, true); ByteBuffer256.Return(this.Values, true);
IntBuffer.Return(this.MaxCodes, true); CodesBuffer16.Return(this.MinCodes, true);
IntBuffer.Return(this.Indices, true); CodesBuffer16.Return(this.MaxCodes, true);
CodesBuffer16.Return(this.Indices, true);
} }
/// <summary> /// <summary>
@ -159,15 +165,20 @@ namespace ImageSharp.Formats.Jpg
decoder.ReadFull(this.Values, 0, this.Length); 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. // Derive the look-up table.
for (int i = 0; i < this.Lut.Length; i++) for (int i = 0; i < this.Lut.Length; i++)
{ {
this.Lut[i] = 0; 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; code <<= 1;
@ -178,8 +189,8 @@ namespace ImageSharp.Formats.Jpg
// whose codeLength's high bits matches code. // whose codeLength's high bits matches code.
// The high 8 bits of lutValue are the encoded value. // The high 8 bits of lutValue are the encoded value.
// The low 8 bits are 1 plus the codeLength. // The low 8 bits are 1 plus the codeLength.
byte base2 = (byte)(code << (7 - i)); int base2 = (code << (7 - i));
ushort lutValue = (ushort)((this.Values[x] << 8) | (2 + i)); int lutValue = (this.ValuesAsInt[x] << 8) | (2 + i);
for (int k = 0; k < 1 << (7 - i); k++) 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]];
}
/// <summary> /// <summary>
/// Initializes the Huffman tree /// Initializes the Huffman tree
/// </summary> /// </summary>
private void Init() private void Init()
{ {
this.Lut = UshortBuffer.Rent(1 << LutSize); this.Lut = IntBuffer256.Rent(MaxNCodes);
this.Values = ByteBuffer.Rent(MaxNCodes); this.Values = ByteBuffer256.Rent(MaxNCodes);
this.MinCodes = IntBuffer.Rent(MaxCodeLength); this.ValuesAsInt = IntBuffer256.Rent(MaxNCodes);
this.MaxCodes = IntBuffer.Rent(MaxCodeLength); this.MinCodes = CodesBuffer16.Rent(MaxCodeLength);
this.Indices = IntBuffer.Rent(MaxCodeLength); this.MaxCodes = CodesBuffer16.Rent(MaxCodeLength);
this.Indices = CodesBuffer16.Rent(MaxCodeLength);
} }
} }
} }

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

@ -85,7 +85,7 @@ namespace ImageSharp.Formats.Jpg
/// <summary> /// <summary>
/// End-of-Band run, specified in section G.1.2.2. /// End-of-Band run, specified in section G.1.2.2.
/// </summary> /// </summary>
private ushort eobRun; private int eobRun;
/// <summary> /// <summary>
/// Pointers to elements of <see cref="data"/> /// Pointers to elements of <see cref="data"/>
@ -349,7 +349,7 @@ 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; int value;
int huffmanIndex = (DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; int huffmanIndex = (DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector;
errorCode = decoder.DecodeHuffmanUnsafe( errorCode = decoder.DecodeHuffmanUnsafe(
ref decoder.HuffmanTrees[huffmanIndex], ref decoder.HuffmanTrees[huffmanIndex],
@ -378,12 +378,12 @@ 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; int value;
errorCode = decoder.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value); errorCode = decoder.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value);
errorCode.EnsureNoEOF(); errorCode.EnsureNoEOF();
byte val0 = (byte)(value >> 4); int val0 = value >> 4;
byte val1 = (byte)(value & 0x0f); int val1 = value & 0x0f;
if (val1 != 0) if (val1 != 0)
{ {
zig += val0; zig += val0;
@ -424,14 +424,14 @@ namespace ImageSharp.Formats.Jpg
private DecoderErrorCode DecodeEobRun(int count, JpegDecoderCore decoder) private DecoderErrorCode DecodeEobRun(int count, JpegDecoderCore decoder)
{ {
uint bitsResult; int bitsResult;
DecoderErrorCode errorCode = decoder.DecodeBitsUnsafe(count, out bitsResult); DecoderErrorCode errorCode = decoder.DecodeBitsUnsafe(count, out bitsResult);
if (errorCode != DecoderErrorCode.NoError) if (errorCode != DecoderErrorCode.NoError)
{ {
return errorCode; return errorCode;
} }
this.eobRun |= (ushort)bitsResult; this.eobRun |= bitsResult;
return DecoderErrorCode.NoError; return DecoderErrorCode.NoError;
} }
@ -548,7 +548,7 @@ namespace ImageSharp.Formats.Jpg
bool done = false; bool done = false;
int z = 0; int z = 0;
byte val; int val;
DecoderErrorCode errorCode = decoder.DecodeHuffmanUnsafe(ref h, out val); DecoderErrorCode errorCode = decoder.DecodeHuffmanUnsafe(ref h, out val);
errorCode.EnsureNoEOF(); errorCode.EnsureNoEOF();
@ -560,7 +560,7 @@ namespace ImageSharp.Formats.Jpg
case 0: case 0:
if (val0 != 0x0f) if (val0 != 0x0f)
{ {
this.eobRun = (ushort)(1 << val0); this.eobRun = 1 << val0;
if (val0 != 0) if (val0 != 0)
{ {
errorCode = this.DecodeEobRun(val0, decoder); errorCode = this.DecodeEobRun(val0, decoder);

5
src/ImageSharp.Formats.Jpeg/JpegConstants.cs

@ -86,6 +86,11 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
public const byte XFF = 0xff; public const byte XFF = 0xff;
/// <summary>
/// Same as <see cref="XFF"/> but of type <see cref="int"/>
/// </summary>
public const int XFFInt = XFF;
/// <summary> /// <summary>
/// Start of Image /// Start of Image
/// </summary> /// </summary>

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

@ -307,7 +307,7 @@ namespace ImageSharp.Formats
/// <param name="count">The number of bits to decode.</param> /// <param name="count">The number of bits to decode.</param>
/// <param name="result">The <see cref="uint" /> result</param> /// <param name="result">The <see cref="uint" /> result</param>
/// <returns>The <see cref="DecoderErrorCode"/></returns> /// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode DecodeBitsUnsafe(int count, out uint result) public DecoderErrorCode DecodeBitsUnsafe(int count, out int result)
{ {
if (this.Bits.UnreadBits < count) if (this.Bits.UnreadBits < count)
{ {
@ -315,7 +315,7 @@ namespace ImageSharp.Formats
} }
result = this.Bits.Accumulator >> (this.Bits.UnreadBits - count); 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.UnreadBits -= count;
this.Bits.Mask >>= count; this.Bits.Mask >>= count;
return DecoderErrorCode.NoError; return DecoderErrorCode.NoError;
@ -327,14 +327,13 @@ namespace ImageSharp.Formats
/// <param name="huffmanTree">The huffman value</param> /// <param name="huffmanTree">The huffman value</param>
/// <param name="result">The decoded <see cref="byte" /></param> /// <param name="result">The decoded <see cref="byte" /></param>
/// <returns>The <see cref="DecoderErrorCode"/></returns> /// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode DecodeHuffmanUnsafe(ref HuffmanTree huffmanTree, out byte result) public DecoderErrorCode DecodeHuffmanUnsafe(ref HuffmanTree huffmanTree, out int result)
{ {
result = 0; result = 0;
// Copy stuff to the stack:
if (huffmanTree.Length == 0) if (huffmanTree.Length == 0)
{ {
throw new ImageFormatException("Uninitialized Huffman table"); DecoderThrowHelper.ThrowImageFormatException.UninitializedHuffmanTable();
} }
if (this.Bits.UnreadBits < 8) if (this.Bits.UnreadBits < 8)
@ -344,14 +343,15 @@ namespace ImageSharp.Formats
if (errorCode == DecoderErrorCode.NoError) 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) if (v != 0)
{ {
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;
result = (byte)(v >> 8); result = v >> 8;
return errorCode; return errorCode;
} }
} }
@ -380,7 +380,8 @@ namespace ImageSharp.Formats
if (code <= huffmanTree.MaxCodes[i]) 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; return DecoderErrorCode.NoError;
} }

Loading…
Cancel
Save