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.
// </copyright>
using System.Runtime.CompilerServices;
namespace ImageSharp.Formats
{
/// <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
/// LSB order.
/// </summary>
internal class Bits
internal struct Bits
{
/// <summary>
/// Gets or sets the accumulator.
/// </summary>
public uint Accumulator { get; set; }
public uint Accumulator;
/// <summary>
/// Gets or sets the mask.
/// <![CDATA[mask==1<<(unreadbits-1) when unreadbits>0, with mask==0 when unreadbits==0.]]>
/// </summary>
public uint Mask { get; set; }
public uint Mask;
/// <summary>
/// Gets or sets the number of unread bits in the accumulator.
/// </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.
// </copyright>
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
namespace ImageSharp.Formats
{
/// <summary>
@ -10,17 +15,27 @@ namespace ImageSharp.Formats
/// has to be able to unread more than 1 byte, due to byte stuffing.
/// Byte stuffing is specified in section F.1.2.3.
/// </summary>
internal class Bytes
internal struct Bytes : IDisposable
{
/// <summary>
/// Initializes a new instance of the <see cref="Bytes"/> class.
/// </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];
this.I = 0;
this.J = 0;
this.UnreadableBytes = 0;
return new Bytes
{
Buffer = ArrayPool.Rent(4096)
};
}
/// <summary>
@ -28,16 +43,128 @@ namespace ImageSharp.Formats
/// buffer[i:j] are the buffered bytes read from the underlying
/// stream that haven't yet been passed further on.
/// </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>
/// Gets or sets the unreadable bytes. The number of bytes to back up i after
/// overshooting. It can be 0, 1 or 2.
/// </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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ImageSharp.Formats
{
@ -107,7 +108,7 @@ namespace ImageSharp.Formats
/// <summary>
/// The byte buffer.
/// </summary>
private readonly Bytes bytes;
internal Bytes bytes;
/// <summary>
/// The image width
@ -137,12 +138,12 @@ namespace ImageSharp.Formats
/// <summary>
/// The input stream.
/// </summary>
private Stream inputStream;
internal Stream inputStream;
/// <summary>
/// Holds the unprocessed bits that have been taken from the byte-stream.
/// </summary>
private Bits bits;
internal Bits bits;
/// <summary>
/// The array of keyline pixels in a CMYK image
@ -209,7 +210,7 @@ namespace ImageSharp.Formats
this.componentArray = new Component[MaxComponents];
this.progCoeffs = new Block8x8F[MaxComponents][];
this.bits = new Bits();
this.bytes = new Bytes();
this.bytes = Bytes.Create();
// TODO: This looks like it could be static.
@ -421,61 +422,7 @@ namespace ImageSharp.Formats
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>
/// Processes a Define Huffman Table marker, and initializes a huffman
/// struct from its contents. Specified in section B.2.4.2.
@ -604,6 +551,8 @@ namespace ImageSharp.Formats
/// <returns>The <see cref="byte"/></returns>
private byte DecodeHuffman(ref Huffman huffman)
{
// Copy stuff to the stack:
if (huffman.Length == 0)
{
throw new ImageFormatException("Uninitialized Huffman table");
@ -611,34 +560,35 @@ namespace ImageSharp.Formats
if (this.bits.UnreadBits < 8)
{
try
{
this.EnsureNBits(8);
ushort v = huffman.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - LutSize)) & 0xff];
//try
//{
var errorCode = bits.EnsureNBits(8, this);
if (v != 0)
if (errorCode == ErrorCodes.NoError)
{
byte n = (byte)((v & 0xff) - 1);
this.bits.UnreadBits -= n;
this.bits.Mask >>= n;
return (byte)(v >> 8);
}
}
catch (MissingFF00Exception)
{
if (this.bytes.UnreadableBytes != 0)
{
this.UnreadByteStuffedByte();
ushort v = huffman.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - LutSize)) & 0xff];
if (v != 0)
{
byte n = (byte)((v & 0xff) - 1);
this.bits.UnreadBits -= n;
this.bits.Mask >>= n;
return (byte)(v >> 8);
}
}
}
catch (ShortHuffmanDataException)
{
if (this.bytes.UnreadableBytes != 0)
else
{
this.UnreadByteStuffedByte();
}
}
//}
//catch (ShortHuffmanDataException) // TODO: This is actually never thrown!
//{
// if (this.bytes.UnreadableBytes != 0)
// {
// this.UnreadByteStuffedByte();
// }
//}
}
int code = 0;
@ -646,7 +596,11 @@ namespace ImageSharp.Formats
{
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)
@ -676,7 +630,11 @@ namespace ImageSharp.Formats
{
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;
@ -694,7 +652,11 @@ namespace ImageSharp.Formats
{
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);
@ -708,32 +670,32 @@ namespace ImageSharp.Formats
/// Fills up the bytes buffer from the underlying stream.
/// It should only be called when there are no unread bytes in bytes.
/// </summary>
private void Fill()
{
if (this.bytes.I != this.bytes.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.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.I = 2;
this.bytes.J = 2;
}
// 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);
if (n == 0)
{
throw new EOFException();
}
this.bytes.J += n;
}
//private void Fill()
//{
// if (this.bytes.I != this.bytes.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.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.I = 2;
// this.bytes.J = 2;
// }
// // 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);
// if (n == 0)
// {
// throw new EOFException();
// }
// this.bytes.J += n;
//}
/// <summary>
/// Undoes the most recent ReadByteStuffedByte call,
@ -754,70 +716,81 @@ namespace ImageSharp.Formats
}
}
/// <summary>
/// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
/// </summary>
/// <returns>The <see cref="byte"/></returns>
private byte ReadByte()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadByte()
{
while (this.bytes.I == this.bytes.J)
{
this.Fill();
}
byte x = this.bytes.Buffer[this.bytes.I];
this.bytes.I++;
this.bytes.UnreadableBytes = 0;
return x;
return bytes.ReadByte(inputStream);
//while (this.bytes.I == this.bytes.J)
//{
// this.Fill();
//}
//byte x = this.bytes.Buffer[this.bytes.I];
//this.bytes.I++;
//this.bytes.UnreadableBytes = 0;
//return x;
}
/// <summary>
/// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data.
/// </summary>
/// <returns>The <see cref="byte"/></returns>
private byte ReadByteStuffedByte()
{
byte x;
// Take the fast path if bytes.buf contains at least two bytes.
if (this.bytes.I + 2 <= this.bytes.J)
{
x = this.bytes.Buffer[this.bytes.I];
this.bytes.I++;
this.bytes.UnreadableBytes = 1;
if (x != JpegConstants.Markers.XFF)
{
return x;
}
if (this.bytes.Buffer[this.bytes.I] != 0x00)
{
throw new MissingFF00Exception();
}
this.bytes.I++;
this.bytes.UnreadableBytes = 2;
return JpegConstants.Markers.XFF;
}
this.bytes.UnreadableBytes = 0;
x = this.ReadByte();
this.bytes.UnreadableBytes = 1;
if (x != JpegConstants.Markers.XFF)
{
return x;
}
x = this.ReadByte();
this.bytes.UnreadableBytes = 2;
if (x != 0x00)
{
throw new MissingFF00Exception();
}
return JpegConstants.Markers.XFF;
}
//internal byte ReadByteStuffedByte(out ErrorCodes errorCode)
//{
// byte x;
// errorCode = ErrorCodes.NoError;
// // Take the fast path if bytes.buf contains at least two bytes.
// if (this.bytes.I + 2 <= this.bytes.J)
// {
// x = this.bytes.Buffer[this.bytes.I];
// this.bytes.I++;
// this.bytes.UnreadableBytes = 1;
// if (x != JpegConstants.Markers.XFF)
// {
// return x;
// }
// if (this.bytes.Buffer[this.bytes.I] != 0x00)
// {
// errorCode = ErrorCodes.MissingFF00;
// return 0;
// //throw new MissingFF00Exception();
// }
// this.bytes.I++;
// this.bytes.UnreadableBytes = 2;
// return JpegConstants.Markers.XFF;
// }
// this.bytes.UnreadableBytes = 0;
// x = this.ReadByte();
// this.bytes.UnreadableBytes = 1;
// if (x != JpegConstants.Markers.XFF)
// {
// return x;
// }
// x = this.ReadByte();
// this.bytes.UnreadableBytes = 2;
// if (x != 0x00)
// {
// errorCode = ErrorCodes.MissingFF00;
// return 0;
// //throw new MissingFF00Exception();
// }
// return JpegConstants.Markers.XFF;
//}
/// <summary>
/// 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)
{
this.UnreadByteStuffedByte();
UnreadByteStuffedByte();
}
this.bytes.UnreadableBytes = 0;
@ -853,7 +826,7 @@ namespace ImageSharp.Formats
length -= 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;
}
this.Fill();
this.bytes.Fill(inputStream);
}
}
@ -1401,6 +1374,7 @@ namespace ImageSharp.Formats
this.AssignResolution(image);
}
/// <summary>
/// Converts the image from the original RBG image pixels.
/// </summary>
@ -1460,7 +1434,10 @@ namespace ImageSharp.Formats
}
}
private BlockF scanWorkerBlock = BlockF.Create();
struct StackallocUnzigData
{
internal fixed int Data [64];
}
/// <summary>
/// Processes the SOS (Start of scan marker).
@ -1593,6 +1570,11 @@ namespace ImageSharp.Formats
Block8x8F temp1 = 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 mx = 0; mx < mxx; mx++)
@ -1648,11 +1630,13 @@ namespace ImageSharp.Formats
var qtIndex = this.componentArray[compIndex].Selector;
// TODO: Find a way to clean up this mess
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])
{
@ -1660,6 +1644,7 @@ namespace ImageSharp.Formats
bp,
&temp1,
&temp2,
unzigPtr,
scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi, bx,
qtp
);
@ -1668,16 +1653,19 @@ namespace ImageSharp.Formats
else
{
b.Clear();
ProcessBlockImpl(ah,
&b,
ProcessBlockImpl(ah,
&b,
&temp1,
&temp2,
unzigPtr,
scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi,
bx, qtp
);
);
}
}
}
@ -1725,14 +1713,16 @@ namespace ImageSharp.Formats
Block8x8F* b,
Block8x8F* temp1,
Block8x8F* temp2,
int* unzigPtr,
Scan[] scan,
int i, int zigStart, int zigEnd, int al,
int[] dc, int compIndex, int @by, int mxx, int hi, int bx,
Block8x8F* qt)
{
var huffmannIdx = AcTable * ThRowSize + scan[i].AcTableSelector;
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
{
@ -1748,7 +1738,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Excessive DC component");
}
int deltaDC = this.ReceiveExtend(value);
int deltaDC = this.bits.ReceiveExtend(value, this);
dc[compIndex] += deltaDC;
//b[0] = dc[compIndex] << al;
@ -1765,7 +1755,7 @@ namespace ImageSharp.Formats
//Huffman huffv = ;
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 val1 = (byte)(value & 0x0f);
if (val1 != 0)
@ -1776,10 +1766,10 @@ namespace ImageSharp.Formats
break;
}
int ac = this.ReceiveExtend(val1);
int ac = this.bits.ReceiveExtend(val1, this);
//b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, Unzig[zig], ac << al);
Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al);
}
else
{
@ -1822,22 +1812,8 @@ namespace ImageSharp.Formats
}
// Dequantize, perform the inverse DCT and store the block to the image.
for (int zig = 0; zig < BlockF.BlockSize; zig++)
{
// 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();
Block8x8F.UnZig(b, qt, unzigPtr);
b->IDCTInto(ref *temp1, ref *temp2);
byte[] dst;
@ -1885,9 +1861,7 @@ namespace ImageSharp.Formats
}
// 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);
}
@ -1954,7 +1928,7 @@ namespace ImageSharp.Formats
/// <param name="zigStart">The zig-zag start index</param>
/// <param name="zigEnd">The zig-zag end index</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.
if (zigStart == 0)
@ -2034,7 +2008,7 @@ namespace ImageSharp.Formats
if (z != 0)
{
//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; }
}
/// <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>
/// The missing ff00 exception.
/// </summary>
private class MissingFF00Exception : Exception
internal class MissingFF00Exception : Exception
{
}
@ -2274,18 +2259,17 @@ namespace ImageSharp.Formats
/// The EOF (End of File exception).
/// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker
/// </summary>
private class EOFException : Exception
internal class EOFException : Exception
{
}
public void Dispose()
{
scanWorkerBlock.Dispose();
for (int i = 0; i < huffmanTrees.Length; i++)
{
huffmanTrees[i].Dispose();
}
bytes.Dispose();
}
}
}

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

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

Loading…
Cancel
Save