Browse Source

woops almost forgot to add the optimizations in DecodeHuffman()

af/merge-core
Anton Firszov 10 years ago
parent
commit
5465ae0c32
  1. 76
      src/ImageSharp/Formats/Jpg/Components/Bits.cs
  2. 15
      src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
  3. 147
      src/ImageSharp/Formats/Jpg/Components/Bytes.cs
  4. 388
      src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
  5. 3
      src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs

76
src/ImageSharp/Formats/Jpg/Components/Bits.cs

@ -3,6 +3,8 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System.Runtime.CompilerServices;
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
/// <summary> /// <summary>
@ -10,22 +12,88 @@ namespace ImageSharp.Formats
/// The n least significant bits of a form the unread bits, to be read in MSB to /// The n least significant bits of a form the unread bits, to be read in MSB to
/// LSB order. /// LSB order.
/// </summary> /// </summary>
internal class Bits internal struct Bits
{ {
/// <summary> /// <summary>
/// Gets or sets the accumulator. /// Gets or sets the accumulator.
/// </summary> /// </summary>
public uint Accumulator { get; set; } public uint 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 { get; set; } public uint 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.
/// </summary> /// </summary>
public int UnreadBits { get; set; } public int UnreadBits;
/// <summary>
/// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at
/// least n. For best performance (avoiding function calls inside hot loops),
/// the caller is the one responsible for first checking that bits.UnreadBits &lt; n.
/// </summary>
/// <param name="n">The number of bits to ensure.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal JpegDecoderCore.ErrorCodes EnsureNBits(int n, JpegDecoderCore decoder)
{
while (true)
{
JpegDecoderCore.ErrorCodes errorCode;
byte c = decoder.bytes.ReadByteStuffedByte(decoder.inputStream, out errorCode);
if (errorCode != JpegDecoderCore.ErrorCodes.NoError)
{
return errorCode;
}
Accumulator = (Accumulator << 8) | c;
UnreadBits += 8;
if (Mask == 0)
{
Mask = 1 << 7;
}
else
{
Mask <<= 8;
}
if (UnreadBits >= n)
{
return JpegDecoderCore.ErrorCodes.NoError;
//break;
}
}
}
internal int ReceiveExtend(byte t, JpegDecoderCore decoder)
{
if (UnreadBits < t)
{
var errorCode = EnsureNBits(t, decoder);
if (errorCode != JpegDecoderCore.ErrorCodes.NoError)
{
throw new JpegDecoderCore.MissingFF00Exception();
}
}
UnreadBits -= t;
Mask >>= t;
int s = 1 << t;
int x = (int)((Accumulator >> UnreadBits) & (s - 1));
if (x < (s >> 1))
{
x += ((-1) << t) + 1;
}
return x;
}
} }
} }

15
src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs

@ -491,6 +491,19 @@ namespace ImageSharp.Formats
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe void UnZig(Block8x8F* block, Block8x8F* qt, int* unzigPtr)
{
float* b = (float*)block;
float* qtp = (float*)qt;
for (int zig = 0; zig < BlockF.BlockSize; zig++)
{
float* unzigPos = b + unzigPtr[zig];
float val = *unzigPos;
val *= qtp[zig];
*unzigPos = val;
}
}
} }
} }

147
src/ImageSharp/Formats/Jpg/Components/Bytes.cs

@ -3,6 +3,11 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
/// <summary> /// <summary>
@ -10,17 +15,27 @@ namespace ImageSharp.Formats
/// has to be able to unread more than 1 byte, due to byte stuffing. /// has to be able to unread more than 1 byte, due to byte stuffing.
/// Byte stuffing is specified in section F.1.2.3. /// Byte stuffing is specified in section F.1.2.3.
/// </summary> /// </summary>
internal class Bytes internal struct Bytes : IDisposable
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Bytes"/> class. /// Initializes a new instance of the <see cref="Bytes"/> class.
/// </summary> /// </summary>
public Bytes() //public Bytes()
//{
// this.Buffer = new byte[4096];
// this.I = 0;
// this.J = 0;
// this.UnreadableBytes = 0;
//}
private static readonly ArrayPool<byte> ArrayPool = ArrayPool<byte>.Create(4096, 50);
public static Bytes Create()
{ {
this.Buffer = new byte[4096]; return new Bytes
this.I = 0; {
this.J = 0; Buffer = ArrayPool.Rent(4096)
this.UnreadableBytes = 0; };
} }
/// <summary> /// <summary>
@ -28,16 +43,128 @@ namespace ImageSharp.Formats
/// buffer[i:j] are the buffered bytes read from the underlying /// buffer[i:j] are the buffered bytes read from the underlying
/// stream that haven't yet been passed further on. /// stream that haven't yet been passed further on.
/// </summary> /// </summary>
public byte[] Buffer { get; set; } public byte[] Buffer;
public int I { get; set; } public int I;
public int J { get; set; } public int J;
/// <summary> /// <summary>
/// Gets or sets the unreadable bytes. The number of bytes to back up i after /// Gets or sets the unreadable bytes. The number of bytes to back up i after
/// overshooting. It can be 0, 1 or 2. /// overshooting. It can be 0, 1 or 2.
/// </summary> /// </summary>
public int UnreadableBytes { get; set; } public int UnreadableBytes;
public void Dispose()
{
if (Buffer!= null) ArrayPool.Return(Buffer);
Buffer = null;
}
/// <summary>
/// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data.
/// </summary>
/// <returns>The <see cref="byte"/></returns>
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadByteStuffedByte(Stream inputStream, out JpegDecoderCore.ErrorCodes errorCode)
{
byte x;
errorCode = JpegDecoderCore.ErrorCodes.NoError;
// Take the fast path if bytes.buf contains at least two bytes.
if (this.I + 2 <= this.J)
{
x = this.Buffer[this.I];
this.I++;
this.UnreadableBytes = 1;
if (x != JpegConstants.Markers.XFF)
{
return x;
}
if (this.Buffer[this.I] != 0x00)
{
errorCode = JpegDecoderCore.ErrorCodes.MissingFF00;
return 0;
//throw new MissingFF00Exception();
}
this.I++;
this.UnreadableBytes = 2;
return JpegConstants.Markers.XFF;
}
this.UnreadableBytes = 0;
x = this.ReadByte(inputStream);
this.UnreadableBytes = 1;
if (x != JpegConstants.Markers.XFF)
{
return x;
}
x = this.ReadByte(inputStream);
this.UnreadableBytes = 2;
if (x != 0x00)
{
errorCode = JpegDecoderCore.ErrorCodes.MissingFF00;
return 0;
//throw new MissingFF00Exception();
}
return JpegConstants.Markers.XFF;
}
/// <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)]
internal byte ReadByte(Stream inputStream)
{
while (this.I == this.J)
{
this.Fill(inputStream);
}
byte x = this.Buffer[this.I];
this.I++;
this.UnreadableBytes = 0;
return x;
}
/// <summary>
/// Fills up the bytes buffer from the underlying stream.
/// It should only be called when there are no unread bytes in bytes.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Fill(Stream inputStream)
{
if (this.I != this.J)
{
throw new ImageFormatException("Fill called when unread bytes exist.");
}
// Move the last 2 bytes to the start of the buffer, in case we need
// to call UnreadByteStuffedByte.
if (this.J > 2)
{
this.Buffer[0] = this.Buffer[this.J - 2];
this.Buffer[1] = this.Buffer[this.J - 1];
this.I = 2;
this.J = 2;
}
// Fill in the rest of the buffer.
int n = inputStream.Read(this.Buffer, this.J, this.Buffer.Length - this.J);
if (n == 0)
{
throw new JpegDecoderCore.EOFException();
}
this.J += n;
}
} }
} }

388
src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs

@ -5,6 +5,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
@ -107,7 +108,7 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// The byte buffer. /// The byte buffer.
/// </summary> /// </summary>
private readonly Bytes bytes; internal Bytes bytes;
/// <summary> /// <summary>
/// The image width /// The image width
@ -137,12 +138,12 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// The input stream. /// The input stream.
/// </summary> /// </summary>
private Stream inputStream; internal Stream inputStream;
/// <summary> /// <summary>
/// Holds the unprocessed bits that have been taken from the byte-stream. /// Holds the unprocessed bits that have been taken from the byte-stream.
/// </summary> /// </summary>
private Bits bits; internal Bits bits;
/// <summary> /// <summary>
/// The array of keyline pixels in a CMYK image /// The array of keyline pixels in a CMYK image
@ -209,7 +210,7 @@ namespace ImageSharp.Formats
this.componentArray = new Component[MaxComponents]; this.componentArray = new Component[MaxComponents];
this.progCoeffs = new Block8x8F[MaxComponents][]; this.progCoeffs = new Block8x8F[MaxComponents][];
this.bits = new Bits(); this.bits = new Bits();
this.bytes = new Bytes(); this.bytes = Bytes.Create();
// TODO: This looks like it could be static. // TODO: This looks like it could be static.
@ -421,61 +422,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Missing SOS marker."); throw new ImageFormatException("Missing SOS marker.");
} }
} }
/// <summary>
/// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at
/// least n. For best performance (avoiding function calls inside hot loops),
/// the caller is the one responsible for first checking that bits.UnreadBits &lt; n.
/// </summary>
/// <param name="n">The number of bits to ensure.</param>
private void EnsureNBits(int n)
{
while (true)
{
byte c = this.ReadByteStuffedByte();
this.bits.Accumulator = (this.bits.Accumulator << 8) | c;
this.bits.UnreadBits += 8;
if (this.bits.Mask == 0)
{
this.bits.Mask = 1 << 7;
}
else
{
this.bits.Mask <<= 8;
}
if (this.bits.UnreadBits >= n)
{
break;
}
}
}
/// <summary>
/// The composition of RECEIVE and EXTEND, specified in section F.2.2.1.
/// </summary>
/// <param name="t">The byte</param>
/// <returns>The <see cref="int"/></returns>
private int ReceiveExtend(byte t)
{
if (this.bits.UnreadBits < t)
{
this.EnsureNBits(t);
}
this.bits.UnreadBits -= t;
this.bits.Mask >>= t;
int s = 1 << t;
int x = (int)((this.bits.Accumulator >> this.bits.UnreadBits) & (s - 1));
if (x < (s >> 1))
{
x += ((-1) << t) + 1;
}
return x;
}
/// <summary> /// <summary>
/// Processes a Define Huffman Table marker, and initializes a huffman /// Processes a Define Huffman Table marker, and initializes a huffman
/// struct from its contents. Specified in section B.2.4.2. /// struct from its contents. Specified in section B.2.4.2.
@ -604,6 +551,8 @@ namespace ImageSharp.Formats
/// <returns>The <see cref="byte"/></returns> /// <returns>The <see cref="byte"/></returns>
private byte DecodeHuffman(ref Huffman huffman) private byte DecodeHuffman(ref Huffman huffman)
{ {
// Copy stuff to the stack:
if (huffman.Length == 0) if (huffman.Length == 0)
{ {
throw new ImageFormatException("Uninitialized Huffman table"); throw new ImageFormatException("Uninitialized Huffman table");
@ -611,34 +560,35 @@ namespace ImageSharp.Formats
if (this.bits.UnreadBits < 8) if (this.bits.UnreadBits < 8)
{ {
try //try
{ //{
this.EnsureNBits(8); var errorCode = bits.EnsureNBits(8, this);
ushort v = huffman.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - LutSize)) & 0xff];
if (v != 0) if (errorCode == ErrorCodes.NoError)
{ {
byte n = (byte)((v & 0xff) - 1); ushort v = huffman.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - LutSize)) & 0xff];
this.bits.UnreadBits -= n;
this.bits.Mask >>= n; if (v != 0)
return (byte)(v >> 8); {
} byte n = (byte)((v & 0xff) - 1);
} this.bits.UnreadBits -= n;
catch (MissingFF00Exception) this.bits.Mask >>= n;
{ return (byte)(v >> 8);
if (this.bytes.UnreadableBytes != 0) }
{
this.UnreadByteStuffedByte();
} }
} else
catch (ShortHuffmanDataException)
{
if (this.bytes.UnreadableBytes != 0)
{ {
this.UnreadByteStuffedByte(); this.UnreadByteStuffedByte();
} }
}
//}
//catch (ShortHuffmanDataException) // TODO: This is actually never thrown!
//{
// if (this.bytes.UnreadableBytes != 0)
// {
// this.UnreadByteStuffedByte();
// }
//}
} }
int code = 0; int code = 0;
@ -646,7 +596,11 @@ namespace ImageSharp.Formats
{ {
if (this.bits.UnreadBits == 0) if (this.bits.UnreadBits == 0)
{ {
this.EnsureNBits(1); var errorCode = this.bits.EnsureNBits(1, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
} }
if ((this.bits.Accumulator & this.bits.Mask) != 0) if ((this.bits.Accumulator & this.bits.Mask) != 0)
@ -676,7 +630,11 @@ namespace ImageSharp.Formats
{ {
if (this.bits.UnreadBits == 0) if (this.bits.UnreadBits == 0)
{ {
this.EnsureNBits(1); var errorCode = this.bits.EnsureNBits(1, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
} }
bool ret = (this.bits.Accumulator & this.bits.Mask) != 0; bool ret = (this.bits.Accumulator & this.bits.Mask) != 0;
@ -694,7 +652,11 @@ namespace ImageSharp.Formats
{ {
if (this.bits.UnreadBits < count) if (this.bits.UnreadBits < count)
{ {
this.EnsureNBits(count); var errorCode = this.bits.EnsureNBits(count, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
} }
uint ret = this.bits.Accumulator >> (this.bits.UnreadBits - count); uint ret = this.bits.Accumulator >> (this.bits.UnreadBits - count);
@ -708,32 +670,32 @@ namespace ImageSharp.Formats
/// 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.
/// </summary> /// </summary>
private void Fill() //private void Fill()
{ //{
if (this.bytes.I != this.bytes.J) // if (this.bytes.I != this.bytes.J)
{ // {
throw new ImageFormatException("Fill called when unread bytes exist."); // throw new ImageFormatException("Fill called when unread bytes exist.");
} // }
// Move the last 2 bytes to the start of the buffer, in case we need // // Move the last 2 bytes to the start of the buffer, in case we need
// to call UnreadByteStuffedByte. // // to call UnreadByteStuffedByte.
if (this.bytes.J > 2) // if (this.bytes.J > 2)
{ // {
this.bytes.Buffer[0] = this.bytes.Buffer[this.bytes.J - 2]; // this.bytes.Buffer[0] = this.bytes.Buffer[this.bytes.J - 2];
this.bytes.Buffer[1] = this.bytes.Buffer[this.bytes.J - 1]; // this.bytes.Buffer[1] = this.bytes.Buffer[this.bytes.J - 1];
this.bytes.I = 2; // this.bytes.I = 2;
this.bytes.J = 2; // this.bytes.J = 2;
} // }
// Fill in the rest of the buffer. // // Fill in the rest of the buffer.
int n = this.inputStream.Read(this.bytes.Buffer, this.bytes.J, this.bytes.Buffer.Length - this.bytes.J); // int n = this.inputStream.Read(this.bytes.Buffer, this.bytes.J, this.bytes.Buffer.Length - this.bytes.J);
if (n == 0) // if (n == 0)
{ // {
throw new EOFException(); // throw new EOFException();
} // }
this.bytes.J += n; // this.bytes.J += n;
} //}
/// <summary> /// <summary>
/// Undoes the most recent ReadByteStuffedByte call, /// Undoes the most recent ReadByteStuffedByte call,
@ -754,70 +716,81 @@ namespace ImageSharp.Formats
} }
} }
/// <summary> /// <summary>
/// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing. /// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
/// </summary> /// </summary>
/// <returns>The <see cref="byte"/></returns> /// <returns>The <see cref="byte"/></returns>
private byte ReadByte() [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadByte()
{ {
while (this.bytes.I == this.bytes.J) return bytes.ReadByte(inputStream);
{ //while (this.bytes.I == this.bytes.J)
this.Fill(); //{
} // this.Fill();
//}
byte x = this.bytes.Buffer[this.bytes.I];
this.bytes.I++; //byte x = this.bytes.Buffer[this.bytes.I];
this.bytes.UnreadableBytes = 0; //this.bytes.I++;
return x; //this.bytes.UnreadableBytes = 0;
//return x;
} }
/// <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>
/// <returns>The <see cref="byte"/></returns> /// <returns>The <see cref="byte"/></returns>
private byte ReadByteStuffedByte() //internal byte ReadByteStuffedByte(out ErrorCodes errorCode)
{ //{
byte x; // byte x;
// Take the fast path if bytes.buf contains at least two bytes. // errorCode = ErrorCodes.NoError;
if (this.bytes.I + 2 <= this.bytes.J)
{ // // Take the fast path if bytes.buf contains at least two bytes.
x = this.bytes.Buffer[this.bytes.I]; // if (this.bytes.I + 2 <= this.bytes.J)
this.bytes.I++; // {
this.bytes.UnreadableBytes = 1; // x = this.bytes.Buffer[this.bytes.I];
if (x != JpegConstants.Markers.XFF) // this.bytes.I++;
{ // this.bytes.UnreadableBytes = 1;
return x; // if (x != JpegConstants.Markers.XFF)
} // {
// return x;
if (this.bytes.Buffer[this.bytes.I] != 0x00) // }
{
throw new MissingFF00Exception(); // if (this.bytes.Buffer[this.bytes.I] != 0x00)
} // {
// errorCode = ErrorCodes.MissingFF00;
this.bytes.I++; // return 0;
this.bytes.UnreadableBytes = 2; // //throw new MissingFF00Exception();
return JpegConstants.Markers.XFF; // }
}
// this.bytes.I++;
this.bytes.UnreadableBytes = 0; // this.bytes.UnreadableBytes = 2;
// return JpegConstants.Markers.XFF;
x = this.ReadByte(); // }
this.bytes.UnreadableBytes = 1;
if (x != JpegConstants.Markers.XFF) // this.bytes.UnreadableBytes = 0;
{
return x; // x = this.ReadByte();
} // this.bytes.UnreadableBytes = 1;
// if (x != JpegConstants.Markers.XFF)
x = this.ReadByte(); // {
this.bytes.UnreadableBytes = 2; // return x;
if (x != 0x00) // }
{
throw new MissingFF00Exception(); // x = this.ReadByte();
} // this.bytes.UnreadableBytes = 2;
// if (x != 0x00)
return JpegConstants.Markers.XFF; // {
} // errorCode = ErrorCodes.MissingFF00;
// return 0;
// //throw new MissingFF00Exception();
// }
// return JpegConstants.Markers.XFF;
//}
/// <summary> /// <summary>
/// Reads exactly length bytes into data. It does not care about byte stuffing. /// Reads exactly length bytes into data. It does not care about byte stuffing.
@ -832,7 +805,7 @@ namespace ImageSharp.Formats
{ {
if (this.bits.UnreadBits >= 8) if (this.bits.UnreadBits >= 8)
{ {
this.UnreadByteStuffedByte(); UnreadByteStuffedByte();
} }
this.bytes.UnreadableBytes = 0; this.bytes.UnreadableBytes = 0;
@ -853,7 +826,7 @@ namespace ImageSharp.Formats
length -= this.bytes.J - this.bytes.I; length -= this.bytes.J - this.bytes.I;
this.bytes.I += this.bytes.J - this.bytes.I; this.bytes.I += this.bytes.J - this.bytes.I;
this.Fill(); this.bytes.Fill(inputStream);
} }
} }
} }
@ -890,7 +863,7 @@ namespace ImageSharp.Formats
break; break;
} }
this.Fill(); this.bytes.Fill(inputStream);
} }
} }
@ -1401,6 +1374,7 @@ namespace ImageSharp.Formats
this.AssignResolution(image); this.AssignResolution(image);
} }
/// <summary> /// <summary>
/// Converts the image from the original RBG image pixels. /// Converts the image from the original RBG image pixels.
/// </summary> /// </summary>
@ -1460,7 +1434,10 @@ namespace ImageSharp.Formats
} }
} }
private BlockF scanWorkerBlock = BlockF.Create(); struct StackallocUnzigData
{
internal fixed int Data [64];
}
/// <summary> /// <summary>
/// Processes the SOS (Start of scan marker). /// Processes the SOS (Start of scan marker).
@ -1593,6 +1570,11 @@ namespace ImageSharp.Formats
Block8x8F temp1 = new Block8x8F(); Block8x8F temp1 = new Block8x8F();
Block8x8F temp2 = new Block8x8F(); Block8x8F temp2 = new Block8x8F();
// Tricky way to copy contents of the Unzig static variable to the stack:
StackallocUnzigData unzigOnStack = new StackallocUnzigData();
int* unzigPtr = unzigOnStack.Data;
Marshal.Copy(Unzig, 0, (IntPtr) unzigPtr, 64);
for (int my = 0; my < myy; my++) for (int my = 0; my < myy; my++)
{ {
for (int mx = 0; mx < mxx; mx++) for (int mx = 0; mx < mxx; mx++)
@ -1648,11 +1630,13 @@ namespace ImageSharp.Formats
var qtIndex = this.componentArray[compIndex].Selector; var qtIndex = this.componentArray[compIndex].Selector;
// TODO: Find a way to clean up this mess // TODO: Find a way to clean up this mess
fixed (Block8x8F* qtp = &this.quantizationTables[qtIndex]) fixed (Block8x8F* qtp = &this.quantizationTables[qtIndex])
{ {
if (this.isProgressive) // Load the previous partially decoded coefficients, if applicable. if (this.isProgressive)
// Load the previous partially decoded coefficients, if applicable.
{ {
blockIndex = ((@by * mxx) * hi) + bx; blockIndex = ((@by*mxx)*hi) + bx;
fixed (Block8x8F* bp = &this.progCoeffs[compIndex][blockIndex]) fixed (Block8x8F* bp = &this.progCoeffs[compIndex][blockIndex])
{ {
@ -1660,6 +1644,7 @@ namespace ImageSharp.Formats
bp, bp,
&temp1, &temp1,
&temp2, &temp2,
unzigPtr,
scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi, bx, scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi, bx,
qtp qtp
); );
@ -1668,16 +1653,19 @@ namespace ImageSharp.Formats
else else
{ {
b.Clear(); b.Clear();
ProcessBlockImpl(ah, ProcessBlockImpl(ah,
&b, &b,
&temp1, &temp1,
&temp2, &temp2,
unzigPtr,
scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi, scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi,
bx, qtp bx, qtp
); );
} }
} }
} }
@ -1725,14 +1713,16 @@ namespace ImageSharp.Formats
Block8x8F* b, Block8x8F* b,
Block8x8F* temp1, Block8x8F* temp1,
Block8x8F* temp2, Block8x8F* temp2,
int* unzigPtr,
Scan[] scan, Scan[] scan,
int i, int zigStart, int zigEnd, int al, int i, int zigStart, int zigEnd, int al,
int[] dc, int compIndex, int @by, int mxx, int hi, int bx, int[] dc, int compIndex, int @by, int mxx, int hi, int bx,
Block8x8F* qt) Block8x8F* qt)
{ {
var huffmannIdx = AcTable * ThRowSize + scan[i].AcTableSelector;
if (ah != 0) if (ah != 0)
{ {
this.Refine(b, ref this.huffmanTrees[AcTable * ThRowSize + scan[i].AcTableSelector], zigStart, zigEnd, 1 << al); this.Refine(b, ref this.huffmanTrees[huffmannIdx], unzigPtr, zigStart, zigEnd, 1 << al);
} }
else else
{ {
@ -1748,7 +1738,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Excessive DC component"); throw new ImageFormatException("Excessive DC component");
} }
int deltaDC = this.ReceiveExtend(value); int deltaDC = this.bits.ReceiveExtend(value, this);
dc[compIndex] += deltaDC; dc[compIndex] += deltaDC;
//b[0] = dc[compIndex] << al; //b[0] = dc[compIndex] << al;
@ -1765,7 +1755,7 @@ namespace ImageSharp.Formats
//Huffman huffv = ; //Huffman huffv = ;
for (; zig <= zigEnd; zig++) for (; zig <= zigEnd; zig++)
{ {
byte value = this.DecodeHuffman(ref this.huffmanTrees[AcTable * ThRowSize + scan[i].AcTableSelector]); byte value = this.DecodeHuffman(ref this.huffmanTrees[huffmannIdx]);
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)
@ -1776,10 +1766,10 @@ namespace ImageSharp.Formats
break; break;
} }
int ac = this.ReceiveExtend(val1); int ac = this.bits.ReceiveExtend(val1, this);
//b[Unzig[zig]] = ac << al; //b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, Unzig[zig], ac << al); Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al);
} }
else else
{ {
@ -1822,22 +1812,8 @@ namespace ImageSharp.Formats
} }
// Dequantize, perform the inverse DCT and store the block to the image. // Dequantize, perform the inverse DCT and store the block to the image.
for (int zig = 0; zig < BlockF.BlockSize; zig++) Block8x8F.UnZig(b, qt, unzigPtr);
{
// TODO: We really need the fancy new corefxlab Span<float> here ...
//b[Unzig[zig]] *= qt[zig];
int unzigIdx = Unzig[zig];
float value = Block8x8F.GetScalarAt(b, unzigIdx);
value *= Block8x8F.GetScalarAt(qt, zig);
Block8x8F.SetScalarAt(b, unzigIdx, value);
}
//IDCT.Transform(ref b);
//FloatIDCT.Transform(ref b);
//ReferenceDCT.IDCT(ref b);
//Block8x8F.SuchIDCT(ref b);
//b->IDCTInplace();
b->IDCTInto(ref *temp1, ref *temp2); b->IDCTInto(ref *temp1, ref *temp2);
byte[] dst; byte[] dst;
@ -1885,9 +1861,7 @@ namespace ImageSharp.Formats
} }
// Level shift by +128, clip to [0, 255], and write to dst. // Level shift by +128, clip to [0, 255], and write to dst.
//temp1->CopyColorsPlz(new MutableSpan<byte>(dst, offset), stride);
temp1->CopyColorsTo(new MutableSpan<byte>(dst, offset), stride, temp2); temp1->CopyColorsTo(new MutableSpan<byte>(dst, offset), stride, temp2);
} }
@ -1954,7 +1928,7 @@ namespace ImageSharp.Formats
/// <param name="zigStart">The zig-zag start index</param> /// <param name="zigStart">The zig-zag start index</param>
/// <param name="zigEnd">The zig-zag end index</param> /// <param name="zigEnd">The zig-zag end index</param>
/// <param name="delta">The low transform offset</param> /// <param name="delta">The low transform offset</param>
private void Refine(Block8x8F* b, ref Huffman h, int zigStart, int zigEnd, int delta) private void Refine(Block8x8F* b, ref Huffman h, int* unzigPtr, int zigStart, int zigEnd, int delta)
{ {
// Refining a DC component is trivial. // Refining a DC component is trivial.
if (zigStart == 0) if (zigStart == 0)
@ -2034,7 +2008,7 @@ namespace ImageSharp.Formats
if (z != 0) if (z != 0)
{ {
//b[Unzig[zig]] = z; //b[Unzig[zig]] = z;
Block8x8F.SetScalarAt(b, Unzig[zig], z); Block8x8F.SetScalarAt(b, unzigPtr[zig], z);
} }
} }
} }
@ -2256,10 +2230,21 @@ namespace ImageSharp.Formats
public byte AcTableSelector { get; set; } public byte AcTableSelector { get; set; }
} }
/// <summary>
/// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent)
/// It's better tho have an error code for this!
/// </summary>
internal enum ErrorCodes
{
NoError,
// ReSharper disable once InconsistentNaming
MissingFF00
}
/// <summary> /// <summary>
/// The missing ff00 exception. /// The missing ff00 exception.
/// </summary> /// </summary>
private class MissingFF00Exception : Exception internal class MissingFF00Exception : Exception
{ {
} }
@ -2274,18 +2259,17 @@ namespace ImageSharp.Formats
/// The EOF (End of File exception). /// The EOF (End of File exception).
/// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker
/// </summary> /// </summary>
private class EOFException : Exception internal class EOFException : Exception
{ {
} }
public void Dispose() public void Dispose()
{ {
scanWorkerBlock.Dispose();
for (int i = 0; i < huffmanTrees.Length; i++) for (int i = 0; i < huffmanTrees.Length; i++)
{ {
huffmanTrees[i].Dispose(); huffmanTrees[i].Dispose();
} }
bytes.Dispose();
} }
} }
} }

3
src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs

@ -246,6 +246,7 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// The AC luminance huffman table index /// The AC luminance huffman table index
/// </summary> /// </summary>
LuminanceAC = 1, LuminanceAC = 1,
// ReSharper restore UnusedMember.Local // ReSharper restore UnusedMember.Local
@ -851,7 +852,6 @@ namespace ImageSharp.Formats
Block b = Block.Create(); Block b = Block.Create();
Block cb = Block.Create(); Block cb = Block.Create();
Block cr = Block.Create(); Block cr = Block.Create();
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
@ -884,7 +884,6 @@ namespace ImageSharp.Formats
Block b = Block.Create(); Block b = Block.Create();
Block[] cb = Block.CreateArray(4); Block[] cb = Block.CreateArray(4);
Block[] cr = Block.CreateArray(4); Block[] cr = Block.CreateArray(4);
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;

Loading…
Cancel
Save