Browse Source

BufferProcessor

pull/90/head
Anton Firszov 9 years ago
parent
commit
fc78a73e49
  1. 41
      src/ImageSharp.Formats.Jpeg/Components/Decoder/Bits.cs
  2. 350
      src/ImageSharp.Formats.Jpeg/Components/Decoder/BufferProcessor.cs
  3. 6
      src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs
  4. 50
      src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs
  5. 356
      src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs
  6. 5
      tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj

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

@ -5,7 +5,6 @@
namespace ImageSharp.Formats.Jpg namespace ImageSharp.Formats.Jpg
{ {
using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
/// <summary> /// <summary>
@ -37,11 +36,11 @@ namespace ImageSharp.Formats.Jpg
/// the caller is the one responsible for first checking that bits.UnreadBits &lt; n. /// the caller is the one responsible for first checking that bits.UnreadBits &lt; n.
/// </summary> /// </summary>
/// <param name="n">The number of bits to ensure.</param> /// <param name="n">The number of bits to ensure.</param>
/// <param name="decoder">Jpeg decoder</param> /// <param name="bufferProcessor">The <see cref="BufferProcessor"/></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnsureNBits(int n, JpegDecoderCore decoder) public void EnsureNBits(int n, ref BufferProcessor bufferProcessor)
{ {
DecoderErrorCode errorCode = this.EnsureNBitsUnsafe(n, decoder); DecoderErrorCode errorCode = this.EnsureNBitsUnsafe(n, ref bufferProcessor);
errorCode.EnsureNoError(); errorCode.EnsureNoError();
} }
@ -52,13 +51,13 @@ namespace ImageSharp.Formats.Jpg
/// This method does not throw. Returns <see cref="DecoderErrorCode"/> instead. /// This method does not throw. Returns <see cref="DecoderErrorCode"/> instead.
/// </summary> /// </summary>
/// <param name="n">The number of bits to ensure.</param> /// <param name="n">The number of bits to ensure.</param>
/// <param name="decoder">Jpeg decoder</param> /// <param name="bufferProcessor">The <see cref="BufferProcessor"/></param>
/// <returns>Error code</returns> /// <returns>Error code</returns>
public DecoderErrorCode EnsureNBitsUnsafe(int n, JpegDecoderCore decoder) public DecoderErrorCode EnsureNBitsUnsafe(int n, ref BufferProcessor bufferProcessor)
{ {
while (true) while (true)
{ {
DecoderErrorCode errorCode = this.EnsureBitsStepImpl(decoder); DecoderErrorCode errorCode = this.EnsureBitsStepImpl(ref bufferProcessor);
if (errorCode != DecoderErrorCode.NoError || this.UnreadBits >= n) if (errorCode != DecoderErrorCode.NoError || this.UnreadBits >= n)
{ {
return errorCode; return errorCode;
@ -69,34 +68,34 @@ namespace ImageSharp.Formats.Jpg
/// <summary> /// <summary>
/// Unrolled version of <see cref="EnsureNBitsUnsafe"/> for n==8 /// Unrolled version of <see cref="EnsureNBitsUnsafe"/> for n==8
/// </summary> /// </summary>
/// <param name="decoder">The <see cref="JpegDecoderCore"/></param> /// <param name="bufferProcessor">The <see cref="BufferProcessor"/></param>
/// <returns>A <see cref="DecoderErrorCode"/></returns> /// <returns>A <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode Ensure8BitsUnsafe(JpegDecoderCore decoder) public DecoderErrorCode Ensure8BitsUnsafe(ref BufferProcessor bufferProcessor)
{ {
return this.EnsureBitsStepImpl(decoder); return this.EnsureBitsStepImpl(ref bufferProcessor);
} }
/// <summary> /// <summary>
/// Unrolled version of <see cref="EnsureNBitsUnsafe"/> for n==1 /// Unrolled version of <see cref="EnsureNBitsUnsafe"/> for n==1
/// </summary> /// </summary>
/// <param name="decoder">The <see cref="JpegDecoderCore"/></param> /// <param name="bufferProcessor">The <see cref="BufferProcessor"/></param>
/// <returns>A <see cref="DecoderErrorCode"/></returns> /// <returns>A <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode Ensure1BitUnsafe(JpegDecoderCore decoder) public DecoderErrorCode Ensure1BitUnsafe(ref BufferProcessor bufferProcessor)
{ {
return this.EnsureBitsStepImpl(decoder); return this.EnsureBitsStepImpl(ref bufferProcessor);
} }
/// <summary> /// <summary>
/// Receive extend /// Receive extend
/// </summary> /// </summary>
/// <param name="t">Byte</param> /// <param name="t">Byte</param>
/// <param name="decoder">Jpeg decoder</param> /// <param name="bufferProcessor">The <see cref="BufferProcessor"/></param>
/// <returns>Read bits value</returns> /// <returns>Read bits value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReceiveExtend(int t, JpegDecoderCore decoder) public int ReceiveExtend(int t, ref BufferProcessor bufferProcessor)
{ {
int x; int x;
DecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, decoder, out x); DecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, ref bufferProcessor, out x);
errorCode.EnsureNoError(); errorCode.EnsureNoError();
return x; return x;
} }
@ -105,14 +104,14 @@ namespace ImageSharp.Formats.Jpg
/// Receive extend /// Receive extend
/// </summary> /// </summary>
/// <param name="t">Byte</param> /// <param name="t">Byte</param>
/// <param name="decoder">Jpeg decoder</param> /// <param name="bufferProcessor">The <see cref="BufferProcessor"/></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(int t, JpegDecoderCore decoder, out int x) public DecoderErrorCode ReceiveExtendUnsafe(int t, ref BufferProcessor bufferProcessor, out int x)
{ {
if (this.UnreadBits < t) if (this.UnreadBits < t)
{ {
DecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, decoder); DecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, ref bufferProcessor);
if (errorCode != DecoderErrorCode.NoError) if (errorCode != DecoderErrorCode.NoError)
{ {
x = int.MaxValue; x = int.MaxValue;
@ -133,10 +132,10 @@ namespace ImageSharp.Formats.Jpg
return DecoderErrorCode.NoError; return DecoderErrorCode.NoError;
} }
private DecoderErrorCode EnsureBitsStepImpl(JpegDecoderCore decoder) private DecoderErrorCode EnsureBitsStepImpl(ref BufferProcessor bufferProcessor)
{ {
int c; int c;
DecoderErrorCode errorCode = decoder.Bytes.ReadByteStuffedByteUnsafe(decoder.InputStream, out c); DecoderErrorCode errorCode = bufferProcessor.Bytes.ReadByteStuffedByteUnsafe(bufferProcessor.InputStream, out c);
if (errorCode != DecoderErrorCode.NoError) if (errorCode != DecoderErrorCode.NoError)
{ {

350
src/ImageSharp.Formats.Jpeg/Components/Decoder/BufferProcessor.cs

@ -0,0 +1,350 @@
// <copyright file="BufferProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Jpg
{
using System;
using System.IO;
using System.Runtime.CompilerServices;
/// <summary>
/// Encapsulates stream reading and processing data and operations for <see cref="JpegDecoderCore"/>.
/// It's a value type for imporved data locality, and reduced number of CALLVIRT-s
/// </summary>
internal struct BufferProcessor : IDisposable
{
/// <summary>
/// Holds the unprocessed bits that have been taken from the byte-stream.
/// </summary>
public Bits Bits;
/// <summary>
/// The byte buffer
/// </summary>
public Bytes Bytes;
/// <summary>
/// Initializes a new instance of the <see cref="BufferProcessor"/> struct.
/// </summary>
/// <param name="inputStream">The input <see cref="Stream"/></param>
/// <param name="temp">Temporal buffer, same as <see cref="JpegDecoderCore.Temp"/></param>
public BufferProcessor(Stream inputStream, byte[] temp)
{
this.Bits = default(Bits);
this.Bytes = Bytes.Create();
this.InputStream = inputStream;
this.Temp = temp;
this.UnexpectedEndOfStreamReached = false;
}
/// <summary>
/// Gets the input stream
/// </summary>
public Stream InputStream { get; }
/// <summary>
/// Gets the temporal buffer, same instance as <see cref="JpegDecoderCore.Temp"/>
/// </summary>
public byte[] Temp { get; }
/// <summary>
/// Gets or sets the value indicating whether an unexpected EOF reached in <see cref="InputStream"/>.
/// </summary>
public bool UnexpectedEndOfStreamReached { get; set; }
/// <summary>
/// If errorCode indicates unexpected EOF, sets <see cref="UnexpectedEndOfStreamReached"/> to true and returns false.
/// Calls <see cref="DecoderThrowHelper.EnsureNoError"/> and returns true otherwise.
/// </summary>
/// <param name="errorCode"></param>
/// <returns></returns>
public bool CheckEOFEnsureNoError(DecoderErrorCode errorCode)
{
if (errorCode == DecoderErrorCode.UnexpectedEndOfStream)
{
this.UnexpectedEndOfStreamReached = true;
return false;
}
errorCode.EnsureNoError();
return true;
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
this.Bytes.Dispose();
}
/// <summary>
/// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
/// </summary>
/// <returns>The <see cref="byte" /></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte ReadByte()
{
return this.Bytes.ReadByte(this.InputStream);
}
/// <summary>
/// Decodes a single bit
/// TODO: This method (and also the usages) could be optimized by batching!
/// </summary>
/// <param name="result">The decoded bit as a <see cref="bool"/></param>
/// <returns>The <see cref="DecoderErrorCode" /></returns>
public DecoderErrorCode DecodeBitUnsafe(out bool result)
{
if (this.Bits.UnreadBits == 0)
{
DecoderErrorCode errorCode = this.Bits.Ensure1BitUnsafe(ref this);
if (errorCode != DecoderErrorCode.NoError)
{
result = false;
return errorCode;
}
}
result = (this.Bits.Accumulator & this.Bits.Mask) != 0;
this.Bits.UnreadBits--;
this.Bits.Mask >>= 1;
return DecoderErrorCode.NoError;
}
/// <summary>
/// Reads exactly length bytes into data. It does not care about byte stuffing.
/// Does not throw on errors, returns <see cref="JpegDecoderCore"/> instead!
/// </summary>
/// <param name="data">The data to write to.</param>
/// <param name="offset">The offset in the source buffer</param>
/// <param name="length">The number of bytes to read</param>
/// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length)
{
// Unread the overshot bytes, if any.
if (this.Bytes.UnreadableBytes != 0)
{
if (this.Bits.UnreadBits >= 8)
{
this.UnreadByteStuffedByte();
}
this.Bytes.UnreadableBytes = 0;
}
DecoderErrorCode errorCode = DecoderErrorCode.NoError;
while (length > 0)
{
if (this.Bytes.J - this.Bytes.I >= length)
{
Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, length);
this.Bytes.I += length;
length -= length;
}
else
{
Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, this.Bytes.J - this.Bytes.I);
offset += this.Bytes.J - this.Bytes.I;
length -= this.Bytes.J - this.Bytes.I;
this.Bytes.I += this.Bytes.J - this.Bytes.I;
errorCode = this.Bytes.FillUnsafe(this.InputStream);
}
}
return errorCode;
}
/// <summary>
/// Decodes the given number of bits
/// </summary>
/// <param name="count">The number of bits to decode.</param>
/// <param name="result">The <see cref="uint" /> result</param>
/// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode DecodeBitsUnsafe(int count, out int result)
{
if (this.Bits.UnreadBits < count)
{
this.Bits.EnsureNBits(count, ref this);
}
result = this.Bits.Accumulator >> (this.Bits.UnreadBits - count);
result = result & ((1 << count) - 1);
this.Bits.UnreadBits -= count;
this.Bits.Mask >>= count;
return DecoderErrorCode.NoError;
}
/// <summary>
/// Extracts the next Huffman-coded value from the bit-stream into result, decoded according to the given value.
/// </summary>
/// <param name="huffmanTree">The huffman value</param>
/// <param name="result">The decoded <see cref="byte" /></param>
/// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode DecodeHuffmanUnsafe(ref HuffmanTree huffmanTree, out int result)
{
result = 0;
if (huffmanTree.Length == 0)
{
DecoderThrowHelper.ThrowImageFormatException.UninitializedHuffmanTable();
}
if (this.Bits.UnreadBits < 8)
{
DecoderErrorCode errorCode = this.Bits.Ensure8BitsUnsafe(ref this);
if (errorCode == DecoderErrorCode.NoError)
{
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 = v >> 8;
return errorCode;
}
}
else
{
this.UnreadByteStuffedByte();
return errorCode;
}
}
int code = 0;
for (int i = 0; i < HuffmanTree.MaxCodeLength; i++)
{
if (this.Bits.UnreadBits == 0)
{
this.Bits.EnsureNBits(1, ref this);
}
if ((this.Bits.Accumulator & this.Bits.Mask) != 0)
{
code |= 1;
}
this.Bits.UnreadBits--;
this.Bits.Mask >>= 1;
if (code <= huffmanTree.MaxCodes[i])
{
result = huffmanTree.GetValue(code, i);
return DecoderErrorCode.NoError;
}
code <<= 1;
}
// Unrecoverable error, throwing:
DecoderThrowHelper.ThrowImageFormatException.BadHuffmanCode();
// DUMMY RETURN! C# doesn't know we have thrown an exception!
return DecoderErrorCode.NoError;
}
/// <summary>
/// Skips the next n bytes.
/// </summary>
/// <param name="count">The number of bytes to ignore.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Skip(int count)
{
DecoderErrorCode errorCode = this.SkipUnsafe(count);
errorCode.EnsureNoError();
}
/// <summary>
/// Skips the next n bytes.
/// Does not throw, returns <see cref="DecoderErrorCode"/> instead!
/// </summary>
/// <param name="count">The number of bytes to ignore.</param>
/// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode SkipUnsafe(int count)
{
// Unread the overshot bytes, if any.
if (this.Bytes.UnreadableBytes != 0)
{
if (this.Bits.UnreadBits >= 8)
{
this.UnreadByteStuffedByte();
}
this.Bytes.UnreadableBytes = 0;
}
while (true)
{
int m = this.Bytes.J - this.Bytes.I;
if (m > count)
{
m = count;
}
this.Bytes.I += m;
count -= m;
if (count == 0)
{
break;
}
DecoderErrorCode errorCode = this.Bytes.FillUnsafe(this.InputStream);
if (errorCode != DecoderErrorCode.NoError)
{
return errorCode;
}
}
return DecoderErrorCode.NoError;
}
/// <summary>
/// Reads exactly length bytes into data. It does not care about byte stuffing.
/// </summary>
/// <param name="data">The data to write to.</param>
/// <param name="offset">The offset in the source buffer</param>
/// <param name="length">The number of bytes to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadFull(byte[] data, int offset, int length)
{
DecoderErrorCode errorCode = this.ReadFullUnsafe(data, offset, length);
errorCode.EnsureNoError();
}
/// <summary>
/// Undoes the most recent ReadByteStuffedByte call,
/// giving a byte of data back from bits to bytes. The Huffman look-up table
/// requires at least 8 bits for look-up, which means that Huffman decoding can
/// sometimes overshoot and read one or two too many bytes. Two-byte overshoot
/// can happen when expecting to read a 0xff 0x00 byte-stuffed byte.
/// </summary>
public void UnreadByteStuffedByte()
{
this.Bytes.I -= this.Bytes.UnreadableBytes;
this.Bytes.UnreadableBytes = 0;
if (this.Bits.UnreadBits >= 8)
{
this.Bits.Accumulator >>= 8;
this.Bits.UnreadBits -= 8;
this.Bits.Mask >>= 8;
}
}
/// <summary>
/// Receive extend
/// </summary>
/// <param name="t">Byte</param>
/// <param name="x">Read bits value</param>
/// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode ReceiveExtendUnsafe(int t, out int x)
{
return this.Bits.ReceiveExtendUnsafe(t, ref this, out x);
}
}
}

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

@ -127,11 +127,11 @@ namespace ImageSharp.Formats.Jpg
/// <summary> /// <summary>
/// Internal part of the DHT processor, whatever does it mean /// Internal part of the DHT processor, whatever does it mean
/// </summary> /// </summary>
/// <param name="decoder">The decoder instance</param> /// <param name="bufferProcessor">The decoder instance</param>
/// <param name="defineHuffmanTablesData">The temporal buffer that holds the data that has been read from the Jpeg stream</param> /// <param name="defineHuffmanTablesData">The temporal buffer that holds the data that has been read from the Jpeg stream</param>
/// <param name="remaining">Remaining bits</param> /// <param name="remaining">Remaining bits</param>
public void ProcessDefineHuffmanTablesMarkerLoop( public void ProcessDefineHuffmanTablesMarkerLoop(
JpegDecoderCore decoder, ref BufferProcessor bufferProcessor,
byte[] defineHuffmanTablesData, byte[] defineHuffmanTablesData,
ref int remaining) ref int remaining)
{ {
@ -163,7 +163,7 @@ namespace ImageSharp.Formats.Jpg
throw new ImageFormatException("DHT has wrong length"); throw new ImageFormatException("DHT has wrong length");
} }
decoder.ReadFull(this.Values, 0, this.Length); bufferProcessor.ReadFull(this.Values, 0, this.Length);
for (int i = 0; i < this.Values.Length; i++) for (int i = 0; i < this.Values.Length; i++)
{ {

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

@ -194,7 +194,15 @@ namespace ImageSharp.Formats.Jpg
} }
} }
// Take an existing block (required when progressive):
int blockIndex = this.GetBlockIndex(decoder);
this.data.Block = decoder.DecodedBlocks[this.ComponentIndex][blockIndex].Block;
this.DecodeBlock(decoder, scanIndex); this.DecodeBlock(decoder, scanIndex);
// Store the decoded block
DecodedBlockMemento[] blocks = decoder.DecodedBlocks[this.ComponentIndex];
DecodedBlockMemento.Store(blocks, blockIndex, this.bx, this.by, ref this.data.Block);
} }
// for j // for j
@ -207,7 +215,7 @@ namespace ImageSharp.Formats.Jpg
{ {
// A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input, // 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. // but this one assumes well-formed input, and hence the restart marker follows immediately.
decoder.ReadFull(decoder.Temp, 0, 2); decoder.BufferProcessor.ReadFull(decoder.Temp, 0, 2);
if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst) if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst)
{ {
@ -221,7 +229,7 @@ namespace ImageSharp.Formats.Jpg
} }
// Reset the Huffman decoder. // Reset the Huffman decoder.
decoder.Bits = default(Bits); decoder.BufferProcessor.Bits = default(Bits);
// Reset the DC components, as per section F.2.1.3.1. // Reset the DC components, as per section F.2.1.3.1.
this.ResetDc(); this.ResetDc();
@ -277,7 +285,7 @@ namespace ImageSharp.Formats.Jpg
throw new ImageFormatException("SOS has wrong length"); throw new ImageFormatException("SOS has wrong length");
} }
decoder.ReadFull(decoder.Temp, 0, remaining); decoder.BufferProcessor.ReadFull(decoder.Temp, 0, remaining);
this.componentScanCount = decoder.Temp[0]; this.componentScanCount = decoder.Temp[0];
int scanComponentCountX2 = 2 * this.componentScanCount; int scanComponentCountX2 = 2 * this.componentScanCount;
@ -334,15 +342,12 @@ namespace ImageSharp.Formats.Jpg
/// <param name="scanIndex">The index of the scan</param> /// <param name="scanIndex">The index of the scan</param>
private void DecodeBlock(JpegDecoderCore decoder, int scanIndex) private void DecodeBlock(JpegDecoderCore decoder, int scanIndex)
{ {
int blockIndex = this.GetBlockIndex(decoder);
this.data.Block = decoder.DecodedBlocks[this.ComponentIndex][blockIndex].Block;
var b = this.pointers.Block; var b = this.pointers.Block;
DecoderErrorCode errorCode; 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)
{ {
this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); this.Refine(ref decoder.BufferProcessor, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al);
} }
else else
{ {
@ -354,7 +359,7 @@ namespace ImageSharp.Formats.Jpg
// Decode the DC coefficient, as specified in section F.2.2.1. // Decode the DC coefficient, as specified in section F.2.2.1.
int 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.BufferProcessor.DecodeHuffmanUnsafe(
ref decoder.HuffmanTrees[huffmanIndex], ref decoder.HuffmanTrees[huffmanIndex],
out value); out value);
errorCode.EnsureNoEOF(); errorCode.EnsureNoEOF();
@ -364,7 +369,9 @@ namespace ImageSharp.Formats.Jpg
throw new ImageFormatException("Excessive DC component"); throw new ImageFormatException("Excessive DC component");
} }
int deltaDC = decoder.Bits.ReceiveExtend(value, decoder); int deltaDC;
errorCode = decoder.BufferProcessor.ReceiveExtendUnsafe(value, out deltaDC);
errorCode.EnsureNoError();
this.pointers.Dc[this.ComponentIndex] += deltaDC; this.pointers.Dc[this.ComponentIndex] += deltaDC;
@ -382,7 +389,7 @@ namespace ImageSharp.Formats.Jpg
for (; zig <= this.zigEnd; zig++) for (; zig <= this.zigEnd; zig++)
{ {
int value; int value;
errorCode = decoder.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value); errorCode = decoder.BufferProcessor.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value);
errorCode.EnsureNoEOF(); errorCode.EnsureNoEOF();
int val0 = value >> 4; int val0 = value >> 4;
@ -395,7 +402,9 @@ namespace ImageSharp.Formats.Jpg
break; break;
} }
int ac = decoder.Bits.ReceiveExtend(val1, decoder); int ac;
errorCode = decoder.BufferProcessor.ReceiveExtendUnsafe(val1, out ac);
errorCode.EnsureNoError();
// b[Unzig[zig]] = ac << al; // b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al); Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al);
@ -407,7 +416,7 @@ namespace ImageSharp.Formats.Jpg
this.eobRun = (ushort)(1 << val0); this.eobRun = (ushort)(1 << val0);
if (val0 != 0) if (val0 != 0)
{ {
errorCode = this.DecodeEobRun(val0, decoder); errorCode = this.DecodeEobRun(val0, ref decoder.BufferProcessor);
errorCode.EnsureNoError(); errorCode.EnsureNoError();
} }
@ -419,13 +428,10 @@ namespace ImageSharp.Formats.Jpg
} }
} }
} }
} }
DecodedBlockMemento[] blocks = decoder.DecodedBlocks[this.ComponentIndex];
DecodedBlockMemento.Store(blocks, blockIndex, this.bx, this.by, ref *b);
} }
private DecoderErrorCode DecodeEobRun(int count, JpegDecoderCore decoder) private DecoderErrorCode DecodeEobRun(int count, ref BufferProcessor decoder)
{ {
int bitsResult; int bitsResult;
DecoderErrorCode errorCode = decoder.DecodeBitsUnsafe(count, out bitsResult); DecoderErrorCode errorCode = decoder.DecodeBitsUnsafe(count, out bitsResult);
@ -513,7 +519,7 @@ namespace ImageSharp.Formats.Jpg
/// <param name="decoder">The decoder instance</param> /// <param name="decoder">The decoder instance</param>
/// <param name="h">The Huffman tree</param> /// <param name="h">The Huffman tree</param>
/// <param name="delta">The low transform offset</param> /// <param name="delta">The low transform offset</param>
private void Refine(JpegDecoderCore decoder, ref HuffmanTree h, int delta) private void Refine(ref BufferProcessor decoder, ref HuffmanTree h, int delta)
{ {
Block8x8F* b = this.pointers.Block; Block8x8F* b = this.pointers.Block;
@ -566,7 +572,7 @@ namespace ImageSharp.Formats.Jpg
this.eobRun = 1 << val0; this.eobRun = 1 << val0;
if (val0 != 0) if (val0 != 0)
{ {
errorCode = this.DecodeEobRun(val0, decoder); errorCode = this.DecodeEobRun(val0, ref decoder);
errorCode.EnsureNoError(); errorCode.EnsureNoError();
} }
@ -596,7 +602,7 @@ namespace ImageSharp.Formats.Jpg
break; break;
} }
zig = this.RefineNonZeroes(decoder, zig, val0, delta); zig = this.RefineNonZeroes(ref decoder, zig, val0, delta);
if (zig > this.zigEnd) if (zig > this.zigEnd)
{ {
throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}"); throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}");
@ -613,7 +619,7 @@ namespace ImageSharp.Formats.Jpg
if (this.eobRun > 0) if (this.eobRun > 0)
{ {
this.eobRun--; this.eobRun--;
this.RefineNonZeroes(decoder, zig, -1, delta); this.RefineNonZeroes(ref decoder, zig, -1, delta);
} }
} }
@ -626,7 +632,7 @@ namespace ImageSharp.Formats.Jpg
/// <param name="nz">The non-zero entry</param> /// <param name="nz">The non-zero entry</param>
/// <param name="delta">The low transform offset</param> /// <param name="delta">The low transform offset</param>
/// <returns>The <see cref="int" /></returns> /// <returns>The <see cref="int" /></returns>
private int RefineNonZeroes(JpegDecoderCore decoder, int zig, int nz, int delta) private int RefineNonZeroes(ref BufferProcessor decoder, int zig, int nz, int delta)
{ {
var b = this.pointers.Block; var b = this.pointers.Block;
for (; zig <= this.zigEnd; zig++) for (; zig <= this.zigEnd; zig++)

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

@ -30,15 +30,12 @@ namespace ImageSharp.Formats
// Complex value type field + mutable + available to other classes = the field MUST NOT be private :P // Complex value type field + mutable + available to other classes = the field MUST NOT be private :P
#pragma warning disable SA1401 // FieldsMustBePrivate #pragma warning disable SA1401 // FieldsMustBePrivate
/// <summary>
/// Holds the unprocessed bits that have been taken from the byte-stream.
/// </summary>
public Bits Bits;
/// <summary> /// <summary>
/// The byte buffer. /// Encapsulates stream reading and processing data and operations for <see cref="JpegDecoderCore"/>.
/// It's a value type for imporved data locality, and reduced number of CALLVIRT-s
/// </summary> /// </summary>
public Bytes Bytes; public BufferProcessor BufferProcessor;
#pragma warning restore SA401 #pragma warning restore SA401
/// <summary> /// <summary>
@ -91,8 +88,6 @@ namespace ImageSharp.Formats
this.Temp = new byte[2 * Block8x8F.ScalarCount]; this.Temp = new byte[2 * Block8x8F.ScalarCount];
this.ComponentArray = new Component[MaxComponents]; this.ComponentArray = new Component[MaxComponents];
this.DecodedBlocks = new DecodedBlockMemento[MaxComponents][]; this.DecodedBlocks = new DecodedBlockMemento[MaxComponents][];
this.Bits = default(Bits);
this.Bytes = Bytes.Create();
} }
/// <summary> /// <summary>
@ -205,194 +200,11 @@ namespace ImageSharp.Formats
} }
this.ycbcrImage?.Dispose(); this.ycbcrImage?.Dispose();
this.Bytes.Dispose(); this.BufferProcessor.Dispose();
this.grayImage.ReturnPooled(); this.grayImage.ReturnPooled();
this.blackImage.ReturnPooled(); this.blackImage.ReturnPooled();
} }
/// <summary>
/// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
/// </summary>
/// <returns>The <see cref="byte" /></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte ReadByte()
{
return this.Bytes.ReadByte(this.InputStream);
}
/// <summary>
/// Decodes a single bit
/// TODO: This method (and also the usages) could be optimized by batching!
/// </summary>
/// <param name="result">The decoded bit as a <see cref="bool"/></param>
/// <returns>The <see cref="DecoderErrorCode" /></returns>
public DecoderErrorCode DecodeBitUnsafe(out bool result)
{
if (this.Bits.UnreadBits == 0)
{
DecoderErrorCode errorCode = this.Bits.Ensure1BitUnsafe(this);
if (errorCode != DecoderErrorCode.NoError)
{
result = false;
return errorCode;
}
}
result = (this.Bits.Accumulator & this.Bits.Mask) != 0;
this.Bits.UnreadBits--;
this.Bits.Mask >>= 1;
return DecoderErrorCode.NoError;
}
/// <summary>
/// Reads exactly length bytes into data. It does not care about byte stuffing.
/// </summary>
/// <param name="data">The data to write to.</param>
/// <param name="offset">The offset in the source buffer</param>
/// <param name="length">The number of bytes to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadFull(byte[] data, int offset, int length)
{
DecoderErrorCode errorCode = this.ReadFullUnsafe(data, offset, length);
errorCode.EnsureNoError();
}
/// <summary>
/// Reads exactly length bytes into data. It does not care about byte stuffing.
/// Does not throw on errors, returns <see cref="JpegDecoderCore"/> instead!
/// </summary>
/// <param name="data">The data to write to.</param>
/// <param name="offset">The offset in the source buffer</param>
/// <param name="length">The number of bytes to read</param>
/// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length)
{
// Unread the overshot bytes, if any.
if (this.Bytes.UnreadableBytes != 0)
{
if (this.Bits.UnreadBits >= 8)
{
this.UnreadByteStuffedByte();
}
this.Bytes.UnreadableBytes = 0;
}
DecoderErrorCode errorCode = DecoderErrorCode.NoError;
while (length > 0)
{
if (this.Bytes.J - this.Bytes.I >= length)
{
Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, length);
this.Bytes.I += length;
length -= length;
}
else
{
Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, this.Bytes.J - this.Bytes.I);
offset += this.Bytes.J - this.Bytes.I;
length -= this.Bytes.J - this.Bytes.I;
this.Bytes.I += this.Bytes.J - this.Bytes.I;
errorCode = this.Bytes.FillUnsafe(this.InputStream);
}
}
return errorCode;
}
/// <summary>
/// Decodes the given number of bits
/// </summary>
/// <param name="count">The number of bits to decode.</param>
/// <param name="result">The <see cref="uint" /> result</param>
/// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode DecodeBitsUnsafe(int count, out int result)
{
if (this.Bits.UnreadBits < count)
{
this.Bits.EnsureNBits(count, this);
}
result = this.Bits.Accumulator >> (this.Bits.UnreadBits - count);
result = result & ((1 << count) - 1);
this.Bits.UnreadBits -= count;
this.Bits.Mask >>= count;
return DecoderErrorCode.NoError;
}
/// <summary>
/// Extracts the next Huffman-coded value from the bit-stream into result, decoded according to the given value.
/// </summary>
/// <param name="huffmanTree">The huffman value</param>
/// <param name="result">The decoded <see cref="byte" /></param>
/// <returns>The <see cref="DecoderErrorCode"/></returns>
public DecoderErrorCode DecodeHuffmanUnsafe(ref HuffmanTree huffmanTree, out int result)
{
result = 0;
if (huffmanTree.Length == 0)
{
DecoderThrowHelper.ThrowImageFormatException.UninitializedHuffmanTable();
}
if (this.Bits.UnreadBits < 8)
{
DecoderErrorCode errorCode = this.Bits.Ensure8BitsUnsafe(this);
if (errorCode == DecoderErrorCode.NoError)
{
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 = v >> 8;
return errorCode;
}
}
else
{
this.UnreadByteStuffedByte();
return errorCode;
}
}
int code = 0;
for (int i = 0; i < HuffmanTree.MaxCodeLength; i++)
{
if (this.Bits.UnreadBits == 0)
{
this.Bits.EnsureNBits(1, this);
}
if ((this.Bits.Accumulator & this.Bits.Mask) != 0)
{
code |= 1;
}
this.Bits.UnreadBits--;
this.Bits.Mask >>= 1;
if (code <= huffmanTree.MaxCodes[i])
{
result = huffmanTree.GetValue(code, i);
return DecoderErrorCode.NoError;
}
code <<= 1;
}
// Unrecoverable error, throwing:
DecoderThrowHelper.ThrowImageFormatException.BadHuffmanCode();
// DUMMY RETURN! C# doesn't know we have thrown an exception!
return DecoderErrorCode.NoError;
}
/// <summary> /// <summary>
/// Gets the <see cref="JpegPixelArea"/> representing the channel at a given component index /// Gets the <see cref="JpegPixelArea"/> representing the channel at a given component index
/// </summary> /// </summary>
@ -462,9 +274,10 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
this.InputStream = stream; this.InputStream = stream;
this.BufferProcessor = new BufferProcessor(stream, this.Temp);
// Check for the Start Of Image marker. // Check for the Start Of Image marker.
this.ReadFull(this.Temp, 0, 2); this.BufferProcessor.ReadFull(this.Temp, 0, 2);
if (this.Temp[0] != JpegConstants.Markers.XFF || this.Temp[1] != JpegConstants.Markers.SOI) if (this.Temp[0] != JpegConstants.Markers.XFF || this.Temp[1] != JpegConstants.Markers.SOI)
{ {
throw new ImageFormatException("Missing SOI marker."); throw new ImageFormatException("Missing SOI marker.");
@ -476,7 +289,7 @@ namespace ImageSharp.Formats
// we can't currently short circute progressive images so don't try. // we can't currently short circute progressive images so don't try.
while (processBytes) while (processBytes)
{ {
this.ReadFull(this.Temp, 0, 2); this.BufferProcessor.ReadFull(this.Temp, 0, 2);
while (this.Temp[0] != 0xff) while (this.Temp[0] != 0xff)
{ {
// Strictly speaking, this is a format error. However, libjpeg is // Strictly speaking, this is a format error. However, libjpeg is
@ -497,7 +310,7 @@ namespace ImageSharp.Formats
// Note that extraneous 0xff bytes in e.g. SOS data are escaped as // Note that extraneous 0xff bytes in e.g. SOS data are escaped as
// "\xff\x00", and so are detected a little further down below. // "\xff\x00", and so are detected a little further down below.
this.Temp[0] = this.Temp[1]; this.Temp[0] = this.Temp[1];
this.Temp[1] = this.ReadByte(); this.Temp[1] = this.BufferProcessor.ReadByte();
} }
byte marker = this.Temp[1]; byte marker = this.Temp[1];
@ -511,7 +324,7 @@ namespace ImageSharp.Formats
{ {
// Section B.1.1.2 says, "Any marker may optionally be preceded by any // Section B.1.1.2 says, "Any marker may optionally be preceded by any
// number of fill bytes, which are bytes assigned code X'FF'". // number of fill bytes, which are bytes assigned code X'FF'".
marker = this.ReadByte(); marker = this.BufferProcessor.ReadByte();
} }
// End Of Image. // End Of Image.
@ -533,7 +346,7 @@ namespace ImageSharp.Formats
// Read the 16-bit length of the segment. The value includes the 2 bytes for the // Read the 16-bit length of the segment. The value includes the 2 bytes for the
// length itself, so we subtract 2 to get the number of remaining bytes. // length itself, so we subtract 2 to get the number of remaining bytes.
this.ReadFull(this.Temp, 0, 2); this.BufferProcessor.ReadFull(this.Temp, 0, 2);
int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2; int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2;
if (remaining < 0) if (remaining < 0)
{ {
@ -556,7 +369,7 @@ namespace ImageSharp.Formats
case JpegConstants.Markers.DHT: case JpegConstants.Markers.DHT:
if (metadataOnly) if (metadataOnly)
{ {
this.Skip(remaining); this.BufferProcessor.Skip(remaining);
} }
else else
{ {
@ -567,7 +380,7 @@ namespace ImageSharp.Formats
case JpegConstants.Markers.DQT: case JpegConstants.Markers.DQT:
if (metadataOnly) if (metadataOnly)
{ {
this.Skip(remaining); this.BufferProcessor.Skip(remaining);
} }
else else
{ {
@ -594,7 +407,7 @@ namespace ImageSharp.Formats
case JpegConstants.Markers.DRI: case JpegConstants.Markers.DRI:
if (metadataOnly) if (metadataOnly)
{ {
this.Skip(remaining); this.BufferProcessor.Skip(remaining);
} }
else else
{ {
@ -615,7 +428,7 @@ namespace ImageSharp.Formats
if ((marker >= JpegConstants.Markers.APP0 && marker <= JpegConstants.Markers.APP15) if ((marker >= JpegConstants.Markers.APP0 && marker <= JpegConstants.Markers.APP15)
|| marker == JpegConstants.Markers.COM) || marker == JpegConstants.Markers.COM)
{ {
this.Skip(remaining); this.BufferProcessor.Skip(remaining);
} }
else if (marker < JpegConstants.Markers.SOF0) else if (marker < JpegConstants.Markers.SOF0)
{ {
@ -632,6 +445,23 @@ namespace ImageSharp.Formats
} }
} }
/// <summary>
/// Processes the SOS (Start of scan marker).
/// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param>
/// <exception cref="ImageFormatException">
/// Missing SOF Marker
/// SOS has wrong length
/// </exception>
private void ProcessStartOfScan(int remaining)
{
JpegScanDecoder scan = default(JpegScanDecoder);
JpegScanDecoder.InitStreamReading(&scan, this, remaining);
this.BufferProcessor.Bits = default(Bits);
this.MakeImage();
scan.DecodeBlocks(this);
}
/// <summary> /// <summary>
/// Process the blocks in <see cref="DecodedBlocks"/> into Jpeg image channels (<see cref="YCbCrImage"/> and <see cref="JpegPixelArea"/>) /// Process the blocks in <see cref="DecodedBlocks"/> into Jpeg image channels (<see cref="YCbCrImage"/> and <see cref="JpegPixelArea"/>)
/// </summary> /// </summary>
@ -1085,11 +915,11 @@ namespace ImageSharp.Formats
{ {
if (remaining < 12) if (remaining < 12)
{ {
this.Skip(remaining); this.BufferProcessor.Skip(remaining);
return; return;
} }
this.ReadFull(this.Temp, 0, 12); this.BufferProcessor.ReadFull(this.Temp, 0, 12);
remaining -= 12; remaining -= 12;
if (this.Temp[0] == 'A' && this.Temp[1] == 'd' && this.Temp[2] == 'o' && this.Temp[3] == 'b' if (this.Temp[0] == 'A' && this.Temp[1] == 'd' && this.Temp[2] == 'o' && this.Temp[3] == 'b'
@ -1101,7 +931,7 @@ namespace ImageSharp.Formats
if (remaining > 0) if (remaining > 0)
{ {
this.Skip(remaining); this.BufferProcessor.Skip(remaining);
} }
} }
@ -1116,12 +946,12 @@ namespace ImageSharp.Formats
{ {
if (remaining < 6) if (remaining < 6)
{ {
this.Skip(remaining); this.BufferProcessor.Skip(remaining);
return; return;
} }
byte[] profile = new byte[remaining]; byte[] profile = new byte[remaining];
this.ReadFull(profile, 0, remaining); this.BufferProcessor.ReadFull(profile, 0, remaining);
if (profile[0] == 'E' && profile[1] == 'x' && profile[2] == 'i' && profile[3] == 'f' && profile[4] == '\0' if (profile[0] == 'E' && profile[1] == 'x' && profile[2] == 'i' && profile[3] == 'f' && profile[4] == '\0'
&& profile[5] == '\0') && profile[5] == '\0')
@ -1138,11 +968,11 @@ namespace ImageSharp.Formats
{ {
if (remaining < 5) if (remaining < 5)
{ {
this.Skip(remaining); this.BufferProcessor.Skip(remaining);
return; return;
} }
this.ReadFull(this.Temp, 0, 13); this.BufferProcessor.ReadFull(this.Temp, 0, 13);
remaining -= 13; remaining -= 13;
// TODO: We should be using constants for this. // TODO: We should be using constants for this.
@ -1157,7 +987,7 @@ namespace ImageSharp.Formats
if (remaining > 0) if (remaining > 0)
{ {
this.Skip(remaining); this.BufferProcessor.Skip(remaining);
} }
} }
@ -1175,7 +1005,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("DHT has wrong length"); throw new ImageFormatException("DHT has wrong length");
} }
this.ReadFull(this.Temp, 0, 17); this.BufferProcessor.ReadFull(this.Temp, 0, 17);
int tc = this.Temp[0] >> 4; int tc = this.Temp[0] >> 4;
if (tc > HuffmanTree.MaxTc) if (tc > HuffmanTree.MaxTc)
@ -1190,7 +1020,10 @@ namespace ImageSharp.Formats
} }
int huffTreeIndex = (tc * HuffmanTree.ThRowSize) + th; int huffTreeIndex = (tc * HuffmanTree.ThRowSize) + th;
this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(this, this.Temp, ref remaining); this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(
ref this.BufferProcessor,
this.Temp,
ref remaining);
} }
} }
@ -1206,7 +1039,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("DRI has wrong length"); throw new ImageFormatException("DRI has wrong length");
} }
this.ReadFull(this.Temp, 0, remaining); this.BufferProcessor.ReadFull(this.Temp, 0, remaining);
this.RestartInterval = ((int)this.Temp[0] << 8) + (int)this.Temp[1]; this.RestartInterval = ((int)this.Temp[0] << 8) + (int)this.Temp[1];
} }
@ -1224,7 +1057,7 @@ namespace ImageSharp.Formats
bool done = false; bool done = false;
remaining--; remaining--;
byte x = this.ReadByte(); byte x = this.BufferProcessor.ReadByte();
int tq = x & 0x0F; int tq = x & 0x0F;
if (tq > MaxTq) if (tq > MaxTq)
{ {
@ -1241,7 +1074,7 @@ namespace ImageSharp.Formats
} }
remaining -= Block8x8F.ScalarCount; remaining -= Block8x8F.ScalarCount;
this.ReadFull(this.Temp, 0, Block8x8F.ScalarCount); this.BufferProcessor.ReadFull(this.Temp, 0, Block8x8F.ScalarCount);
for (int i = 0; i < Block8x8F.ScalarCount; i++) for (int i = 0; i < Block8x8F.ScalarCount; i++)
{ {
@ -1257,7 +1090,7 @@ namespace ImageSharp.Formats
} }
remaining -= 2 * Block8x8F.ScalarCount; remaining -= 2 * Block8x8F.ScalarCount;
this.ReadFull(this.Temp, 0, 2 * Block8x8F.ScalarCount); this.BufferProcessor.ReadFull(this.Temp, 0, 2 * Block8x8F.ScalarCount);
for (int i = 0; i < Block8x8F.ScalarCount; i++) for (int i = 0; i < Block8x8F.ScalarCount; i++)
{ {
@ -1307,7 +1140,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Incorrect number of components"); throw new ImageFormatException("Incorrect number of components");
} }
this.ReadFull(this.Temp, 0, remaining); this.BufferProcessor.ReadFull(this.Temp, 0, remaining);
// We only support 8-bit precision. // We only support 8-bit precision.
if (this.Temp[0] != 8) if (this.Temp[0] != 8)
@ -1484,96 +1317,5 @@ namespace ImageSharp.Formats
this.DecodedBlocks[i] = DecodedBlockMemento.RentArray(size); this.DecodedBlocks[i] = DecodedBlockMemento.RentArray(size);
} }
} }
/// <summary>
/// Processes the SOS (Start of scan marker).
/// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param>
/// <exception cref="ImageFormatException">
/// Missing SOF Marker
/// SOS has wrong length
/// </exception>
private void ProcessStartOfScan(int remaining)
{
JpegScanDecoder scan = default(JpegScanDecoder);
JpegScanDecoder.InitStreamReading(&scan, this, remaining);
this.Bits = default(Bits);
this.MakeImage();
scan.DecodeBlocks(this);
}
/// <summary>
/// Skips the next n bytes.
/// </summary>
/// <param name="count">The number of bytes to ignore.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Skip(int count)
{
DecoderErrorCode errorCode = this.SkipUnsafe(count);
errorCode.EnsureNoError();
}
/// <summary>
/// Skips the next n bytes.
/// Does not throw, returns <see cref="DecoderErrorCode"/> instead!
/// </summary>
/// <param name="count">The number of bytes to ignore.</param>
/// <returns>The <see cref="DecoderErrorCode"/></returns>
private DecoderErrorCode SkipUnsafe(int count)
{
// Unread the overshot bytes, if any.
if (this.Bytes.UnreadableBytes != 0)
{
if (this.Bits.UnreadBits >= 8)
{
this.UnreadByteStuffedByte();
}
this.Bytes.UnreadableBytes = 0;
}
while (true)
{
int m = this.Bytes.J - this.Bytes.I;
if (m > count)
{
m = count;
}
this.Bytes.I += m;
count -= m;
if (count == 0)
{
break;
}
DecoderErrorCode errorCode = this.Bytes.FillUnsafe(this.InputStream);
if (errorCode != DecoderErrorCode.NoError)
{
return errorCode;
}
}
return DecoderErrorCode.NoError;
}
/// <summary>
/// Undoes the most recent ReadByteStuffedByte call,
/// giving a byte of data back from bits to bytes. The Huffman look-up table
/// requires at least 8 bits for look-up, which means that Huffman decoding can
/// sometimes overshoot and read one or two too many bytes. Two-byte overshoot
/// can happen when expecting to read a 0xff 0x00 byte-stuffed byte.
/// </summary>
private void UnreadByteStuffedByte()
{
this.Bytes.I -= this.Bytes.UnreadableBytes;
this.Bytes.UnreadableBytes = 0;
if (this.Bits.UnreadBits >= 8)
{
this.Bits.Accumulator >>= 8;
this.Bits.UnreadBits -= 8;
this.Bits.Mask >>= 8;
}
}
} }
} }

5
tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj

@ -317,7 +317,6 @@
<None Include="app.config" /> <None Include="app.config" />
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="README.md" /> <None Include="README.md" />
<None Include="_TraceEventProgrammersGuide.docx" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
@ -329,10 +328,6 @@
<Analyzer Include="..\..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" /> <Analyzer Include="..\..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" />
<Analyzer Include="..\..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.Analyzers.dll" /> <Analyzer Include="..\..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.Analyzers.dll" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="TraceEvent.ReadMe.txt" />
<Content Include="TraceEvent.ReleaseNotes.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup> <PropertyGroup>

Loading…
Cancel
Save