Browse Source

almost worx

af/merge-core
antonfirsov 9 years ago
parent
commit
1fe06317f1
  1. 7
      src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
  2. 447
      src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs
  3. 174
      src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
  4. 16
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

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

@ -373,5 +373,12 @@ namespace ImageSharp.Formats.Jpg
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy(Block8x8F* dest, Block8x8F* source)
{
*dest = *source;
//Unsafe.CopyBlock(dest, source, (uint)sizeof(Block8x8F));
}
}
}

447
src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs

@ -1,9 +1,12 @@
namespace ImageSharp.Formats.Jpg
{
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
internal unsafe struct DecoderScanProcessor
{
[StructLayout(LayoutKind.Sequential)]
public struct ComponentData
{
public Block8x8F Block;
@ -12,11 +15,13 @@
public Block8x8F Temp2;
public Block8x8F QuantiazationTable;
public UnzigData Unzig;
public fixed byte ScanData [3 * JpegDecoderCore.MaxComponents];
//public fixed byte ScanData [3 * JpegDecoderCore.MaxComponents];
public fixed int Dc [JpegDecoderCore.MaxComponents];
//public fixed int Dc [JpegDecoderCore.MaxComponents];
public static ComponentData Create()
{
@ -24,7 +29,6 @@
data.Unzig = UnzigData.Create();
return data;
}
}
public struct ComponentPointers
@ -35,23 +39,31 @@
public Block8x8F* Temp2;
public Block8x8F* QuantiazationTable;
public int* Unzig;
public Scan* Scan;
//public Scan* Scan;
public int* Dc;
//public int* Dc;
public ComponentPointers(ComponentData* basePtr)
{
this.Block = &basePtr->Block;
this.Temp1 = &basePtr->Temp1;
this.Temp2 = &basePtr->Temp2;
this.QuantiazationTable = &basePtr->QuantiazationTable;
this.Unzig = basePtr->Unzig.Data;
this.Scan = (Scan*) basePtr->ScanData;
this.Dc = basePtr->Dc;
//this.Scan = (Scan*) basePtr->ScanData;
//this.Dc = basePtr->Dc;
}
}
//private void ResetDc()
//{
// Unsafe.InitBlock(this.Pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents);
//}
public int bx;
public int by;
@ -68,17 +80,20 @@
public int myy;
public int scanComponentCount;
public ComponentData Data;
public ComponentPointers Pointers;
public static void Init(DecoderScanProcessor* p, JpegDecoderCore decoder, int remaining)
public static void Init(DecoderScanProcessor* p, JpegDecoderCore decoder, int remaining, Scan[] scan)
{
p->Data = ComponentData.Create();
p->Pointers = new ComponentPointers(&p->Data);
p->InitImpl(decoder, remaining, scan);
}
private void InitCommon(JpegDecoderCore decoder, int remaining)
private void InitImpl(JpegDecoderCore decoder, int remaining, Scan[] scan)
{
if (decoder.ComponentCount == 0)
{
@ -91,9 +106,9 @@
}
decoder.ReadFull(decoder.Temp, 0, remaining);
byte scanComponentCount = decoder.Temp[0];
this.scanComponentCount = decoder.Temp[0];
int scanComponentCountX2 = 2 * scanComponentCount;
int scanComponentCountX2 = 2 * this.scanComponentCount;
if (remaining != 4 + scanComponentCountX2)
{
throw new ImageFormatException("SOS length inconsistent with number of components");
@ -101,9 +116,9 @@
int totalHv = 0;
for (int i = 0; i < scanComponentCount; i++)
for (int i = 0; i < this.scanComponentCount; i++)
{
this.ProcessScanImpl(decoder, i, ref this.Pointers.Scan[i], ref totalHv);
this.ProcessScanImpl(decoder, i, ref scan[i], ref totalHv, scan);
}
// Section B.2.3 states that if there is more than one component then the
// total H*V values in a scan must be <= 10.
@ -127,7 +142,7 @@
throw new ImageFormatException("Bad spectral selection bounds");
}
if (this.zigStart != 0 && scanComponentCount != 1)
if (this.zigStart != 0 && this.scanComponentCount != 1)
{
throw new ImageFormatException("Progressive AC coefficients for more than one component");
}
@ -146,12 +161,12 @@
if (decoder.IsProgressive)
{
for (int i = 0; i < scanComponentCount; i++)
for (int i = 0; i < this.scanComponentCount; i++)
{
int compIndex = this.Pointers.Scan[i].Index;
int compIndex = scan[i].Index;
if (decoder.ProgCoeffs[compIndex] == null)
{
int size = mxx * myy * decoder.ComponentArray[compIndex].HorizontalFactor
int size = this.mxx * this.myy * decoder.ComponentArray[compIndex].HorizontalFactor
* decoder.ComponentArray[compIndex].VerticalFactor;
decoder.ProgCoeffs[compIndex] = new Block8x8F[size];
@ -160,7 +175,7 @@
}
}
private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv)
private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv, Scan[] scan)
{
// Component selector.
int cs = decoder.Temp[1 + (2 * i)];
@ -181,7 +196,7 @@
currentScan.Index = (byte)compIndex;
this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex]);
this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex], scan);
}
@ -190,7 +205,7 @@
int i,
ref Scan currentScan,
ref int totalHv,
ref Component currentComponent)
ref Component currentComponent, Scan[] scan)
{
// Section B.2.3 states that "the value of Cs_j shall be different from
// the values of Cs_1 through Cs_(j-1)". Since we have previously
@ -199,7 +214,7 @@
// into comp are unique.
for (int j = 0; j < i; j++)
{
if (currentScan.Index == this.Pointers.Scan[j].Index)
if (currentScan.Index == scan[j].Index)
{
throw new ImageFormatException("Repeated component selector");
}
@ -220,9 +235,391 @@
}
}
private void InitProgressive(JpegDecoderCore decoder)
public void ProcessBlocks(JpegDecoderCore decoder, Scan[] scan, ref int[] dc)
{
int blockCount = 0;
int mcu = 0;
byte expectedRst = JpegConstants.Markers.RST0;
for (int my = 0; my < this.myy; my++)
{
for (int mx = 0; mx < this.mxx; mx++)
{
for (int i = 0; i < this.scanComponentCount; i++)
{
int compIndex = scan[i].Index;
int hi = decoder.ComponentArray[compIndex].HorizontalFactor;
int vi = decoder.ComponentArray[compIndex].VerticalFactor;
for (int j = 0; j < hi * vi; j++)
{
// The blocks are traversed one MCU at a time. For 4:2:0 chroma
// subsampling, there are four Y 8x8 blocks in every 16x16 MCU.
// For a baseline 32x16 pixel image, the Y blocks visiting order is:
// 0 1 4 5
// 2 3 6 7
// For progressive images, the interleaved scans (those with component count > 1)
// are traversed as above, but non-interleaved scans are traversed left
// to right, top to bottom:
// 0 1 2 3
// 4 5 6 7
// Only DC scans (zigStart == 0) can be interleave AC scans must have
// only one component.
// To further complicate matters, for non-interleaved scans, there is no
// data for any blocks that are inside the image at the MCU level but
// outside the image at the pixel level. For example, a 24x16 pixel 4:2:0
// progressive image consists of two 16x16 MCUs. The interleaved scans
// will process 8 Y blocks:
// 0 1 4 5
// 2 3 6 7
// The non-interleaved scans will process only 6 Y blocks:
// 0 1 2
// 3 4 5
if (this.scanComponentCount != 1)
{
this.bx = (hi * mx) + (j % hi);
this.by = (vi * my) + (j / hi);
}
else
{
int q = this.mxx * hi;
this.bx = blockCount % q;
this.by = blockCount / q;
blockCount++;
if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight)
{
continue;
}
}
int qtIndex = decoder.ComponentArray[compIndex].Selector;
// TODO: Reading & processing blocks should be done in 2 separate loops. The second one could be parallelized. The first one could be async.
this.Data.QuantiazationTable = decoder.QuantizationTables[qtIndex];
//Load the previous partially decoded coefficients, if applicable.
if (decoder.IsProgressive)
{
int blockIndex = ((this.by * this.mxx) * hi) + this.bx;
this.Data.Block = decoder.ProgCoeffs[compIndex][blockIndex];
}
else
{
this.Data.Block.Clear();
}
this.ProcessBlockImpl(decoder, i, compIndex, hi, scan, ref dc);
//fixed (Block8x8F* qtp = &decoder.QuantizationTables[qtIndex])
//{
// // Load the previous partially decoded coefficients, if applicable.
// if (decoder.IsProgressive)
// {
// int blockIndex = ((@by * mxx) * hi) + bx;
// fixed (Block8x8F* bp = &decoder.ProgCoeffs[compIndex][blockIndex])
// {
// Unsafe.CopyBlock(this.Pointers.Block, bp, (uint)sizeof(Block8x8F));
// }
// }
// else
// {
// this.Data.Block.Clear();
// }
// decoder.ProcessBlockImpl(this.ah, this.Pointers.Block, this.Pointers.Temp1, this.Pointers.Temp2, this.Pointers.Unzig, scan, i, this.zigStart, this.zigEnd, this.al, dc, compIndex, this.@by, this.mxx, hi, this.bx, qtp);
//}
}
// for j
}
// for i
mcu++;
if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < this.mxx * this.myy)
{
// 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.
decoder.ReadFull(decoder.Temp, 0, 2);
if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst)
{
throw new ImageFormatException("Bad RST marker");
}
expectedRst++;
if (expectedRst == JpegConstants.Markers.RST7 + 1)
{
expectedRst = JpegConstants.Markers.RST0;
}
// Reset the Huffman decoder.
decoder.Bits = default(Bits);
// Reset the DC components, as per section F.2.1.3.1.
//this.ResetDc();
dc = new int[JpegDecoderCore.MaxComponents];
// Reset the progressive decoder state, as per section G.1.2.2.
decoder.EobRun = 0;
}
}
// for mx
}
}
/// <summary>
/// The AC table index
/// </summary>
internal const int AcTable = 1;
/// <summary>
/// The DC table index
/// </summary>
internal const int DcTable = 0;
private void Refine(JpegDecoderCore decoder, ref HuffmanTree h, int delta)
{
Block8x8F* b = this.Pointers.Block;
// Refining a DC component is trivial.
if (this.zigStart == 0)
{
if (this.zigEnd != 0)
{
throw new ImageFormatException("Invalid state for zig DC component");
}
bool bit = decoder.DecodeBit();
if (bit)
{
int stuff = (int)Block8x8F.GetScalarAt(b, 0);
// int stuff = (int)b[0];
stuff |= delta;
// b[0] = stuff;
Block8x8F.SetScalarAt(b, 0, stuff);
}
return;
}
// Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3.
int zig = this.zigStart;
if (decoder.EobRun == 0)
{
for (; zig <= this.zigEnd; zig++)
{
bool done = false;
int z = 0;
byte val = decoder.DecodeHuffman(ref h);
int val0 = val >> 4;
int val1 = val & 0x0f;
switch (val1)
{
case 0:
if (val0 != 0x0f)
{
decoder.EobRun = (ushort)(1 << val0);
if (val0 != 0)
{
decoder.EobRun |= (ushort)decoder.DecodeBits(val0);
}
done = true;
}
break;
case 1:
z = delta;
bool bit = decoder.DecodeBit();
if (!bit)
{
z = -z;
}
break;
default:
throw new ImageFormatException("Unexpected Huffman code");
}
if (done)
{
break;
}
int blah = zig;
zig = this.RefineNonZeroes(decoder, zig, val0, delta);
if (zig > this.zigEnd)
{
throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}");
}
if (z != 0)
{
// b[Unzig[zig]] = z;
Block8x8F.SetScalarAt(b, this.Pointers.Unzig[zig], z);
}
}
}
if (decoder.EobRun > 0)
{
decoder.EobRun--;
this.RefineNonZeroes(decoder, zig,-1, delta);
}
}
private int RefineNonZeroes(JpegDecoderCore decoder, int zig, int nz, int delta)
{
var b = this.Pointers.Block;
for (; zig <= zigEnd; zig++)
{
int u = this.Pointers.Unzig[zig];
float bu = Block8x8F.GetScalarAt(b, u);
// TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary?
if (bu == 0)
{
if (nz == 0)
{
break;
}
nz--;
continue;
}
bool bit = decoder.DecodeBit();
if (!bit)
{
continue;
}
if (bu >= 0)
{
// b[u] += delta;
Block8x8F.SetScalarAt(b, u, bu + delta);
}
else
{
// b[u] -= delta;
Block8x8F.SetScalarAt(b, u, bu - delta);
}
}
return zig;
}
private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi, Scan[] scan, ref int[] dc)
{
var b = this.Pointers.Block;
//var dc = this.Pointers.Dc;
int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector;
if (this.ah != 0)
{
this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al);
}
else
{
int zig = zigStart;
if (zig == 0)
{
zig++;
// Decode the DC coefficient, as specified in section F.2.2.1.
byte value =
decoder.DecodeHuffman(
ref decoder.HuffmanTrees[(DcTable * HuffmanTree.ThRowSize) + scan[i].DcTableSelector]);
if (value > 16)
{
throw new ImageFormatException("Excessive DC component");
}
int deltaDC = decoder.Bits.ReceiveExtend(value, decoder);
dc[compIndex] += deltaDC;
// b[0] = dc[compIndex] << al;
Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al);
}
if (zig <= this.zigEnd && decoder.EobRun > 0)
{
decoder.EobRun--;
}
else
{
// Decode the AC coefficients, as specified in section F.2.2.2.
// Huffman huffv = ;
for (; zig <= this.zigEnd; zig++)
{
byte value = decoder.DecodeHuffman(ref decoder.HuffmanTrees[huffmannIdx]);
byte val0 = (byte)(value >> 4);
byte val1 = (byte)(value & 0x0f);
if (val1 != 0)
{
zig += val0;
if (zig > this.zigEnd)
{
break;
}
int ac = decoder.Bits.ReceiveExtend(val1, decoder);
// b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, this.Pointers.Unzig[zig], ac << this.al);
}
else
{
if (val0 != 0x0f)
{
decoder.EobRun = (ushort)(1 << val0);
if (val0 != 0)
{
decoder.EobRun |= (ushort)decoder.DecodeBits(val0);
}
decoder.EobRun--;
break;
}
zig += 0x0f;
}
}
}
}
if (decoder.IsProgressive)
{
if (this.zigEnd != Block8x8F.ScalarCount - 1 || this.al != 0)
{
// We haven't completely decoded this 8x8 block. Save the coefficients.
// this.ProgCoeffs[compIndex][((@by * mxx) * hi) + bx] = b.Clone();
decoder.ProgCoeffs[compIndex][((this.by * this.mxx) * hi) + this.bx] = *b;
// At this point, we could execute the rest of the loop body to dequantize and
// perform the inverse DCT, to save early stages of a progressive image to the
// *image.YCbCr buffers (the whole point of progressive encoding), but in Go,
// the jpeg.Decode function does not return until the entire image is decoded,
// so we "continue" here to avoid wasted computation.
return;
}
}
// Dequantize, perform the inverse DCT and store the block to the image.
Block8x8F.UnZig(b, this.Pointers.QuantiazationTable, this.Pointers.Unzig);
DCT.TransformIDCT(ref *b, ref *this.Pointers.Temp1, ref *this.Pointers.Temp2);
var destChannel = decoder.GetDestinationChannel(compIndex);
var destArea = destChannel.GetOffsetedSubAreaForBlock(this.bx, this.by);
destArea.LoadColorsFrom(this.Pointers.Temp1, this.Pointers.Temp2);
}
}

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

@ -7,6 +7,7 @@ namespace ImageSharp.Formats
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using ImageSharp.Formats.Jpg;
@ -19,12 +20,12 @@ namespace ImageSharp.Formats
/// <summary>
/// The AC table index
/// </summary>
private const int AcTable = 1;
internal const int AcTable = 1;
/// <summary>
/// The DC table index
/// </summary>
private const int DcTable = 0;
internal const int DcTable = 0;
/// <summary>
/// The maximum number of color components
@ -44,7 +45,7 @@ namespace ImageSharp.Formats
/// <summary>
/// The huffman trees
/// </summary>
private readonly HuffmanTree[] huffmanTrees;
internal HuffmanTree[] HuffmanTrees { get; }
/// <summary>
/// Saved state between progressive-mode scans.
@ -76,11 +77,11 @@ namespace ImageSharp.Formats
/// <summary>
/// Holds the unprocessed bits that have been taken from the byte-stream.
/// </summary>
private Bits bits;
internal Bits Bits;
private JpegPixelArea blackImage;
private int blockIndex;
//private int blockIndex;
/// <summary>
/// The byte buffer.
@ -95,7 +96,7 @@ namespace ImageSharp.Formats
/// <summary>
/// End-of-Band run, specified in section G.1.2.2.
/// </summary>
private ushort eobRun;
internal ushort EobRun;
/// <summary>
/// A grayscale image to decode to.
@ -135,7 +136,7 @@ namespace ImageSharp.Formats
/// <summary>
/// The restart interval
/// </summary>
private int restartInterval;
internal int RestartInterval { get; private set; }
/// <summary>
/// The vertical resolution. Calculated if the image has a JFIF header.
@ -152,15 +153,15 @@ namespace ImageSharp.Formats
/// </summary>
public JpegDecoderCore()
{
this.huffmanTrees = HuffmanTree.CreateHuffmanTrees();
this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees();
this.QuantizationTables = new Block8x8F[MaxTq + 1];
this.Temp = new byte[2 * Block8x8F.ScalarCount];
this.ComponentArray = new Component[MaxComponents];
this.ProgCoeffs = new Block8x8F[MaxComponents][];
this.bits = default(Bits);
this.Bits = default(Bits);
this.bytes = Bytes.Create();
}
/// <summary>
/// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent)
/// It's better tho have an error code for this!
@ -333,7 +334,7 @@ namespace ImageSharp.Formats
return;
}
this.ProcessStartOfScan(remaining);
this.ProcessStartOfScan22(remaining);
break;
case JpegConstants.Markers.DRI:
if (configOnly)
@ -430,9 +431,9 @@ namespace ImageSharp.Formats
/// </summary>
public void Dispose()
{
for (int i = 0; i < this.huffmanTrees.Length; i++)
for (int i = 0; i < this.HuffmanTrees.Length; i++)
{
this.huffmanTrees[i].Dispose();
this.HuffmanTrees[i].Dispose();
}
this.ycbcrImage?.Dispose();
@ -440,7 +441,7 @@ namespace ImageSharp.Formats
this.grayImage.ReturnPooled();
this.blackImage.ReturnPooled();
}
/// <summary>
/// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
/// </summary>
@ -462,7 +463,7 @@ namespace ImageSharp.Formats
// Unread the overshot bytes, if any.
if (this.bytes.UnreadableBytes != 0)
{
if (this.bits.UnreadBits >= 8)
if (this.Bits.UnreadBits >= 8)
{
this.UnreadByteStuffedByte();
}
@ -727,20 +728,20 @@ namespace ImageSharp.Formats
/// Decodes a single bit
/// </summary>
/// <returns>The <see cref="bool" /></returns>
private bool DecodeBit()
internal bool DecodeBit()
{
if (this.bits.UnreadBits == 0)
if (this.Bits.UnreadBits == 0)
{
ErrorCodes errorCode = this.bits.EnsureNBits(1, this);
ErrorCodes errorCode = this.Bits.EnsureNBits(1, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
}
bool ret = (this.bits.Accumulator & this.bits.Mask) != 0;
this.bits.UnreadBits--;
this.bits.Mask >>= 1;
bool ret = (this.Bits.Accumulator & this.Bits.Mask) != 0;
this.Bits.UnreadBits--;
this.Bits.Mask >>= 1;
return ret;
}
@ -749,21 +750,21 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="count">The number of bits to decode.</param>
/// <returns>The <see cref="uint" /></returns>
private uint DecodeBits(int count)
internal uint DecodeBits(int count)
{
if (this.bits.UnreadBits < count)
if (this.Bits.UnreadBits < count)
{
ErrorCodes errorCode = this.bits.EnsureNBits(count, this);
ErrorCodes 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);
ret = (uint)(ret & ((1 << count) - 1));
this.bits.UnreadBits -= count;
this.bits.Mask >>= count;
this.Bits.UnreadBits -= count;
this.Bits.Mask >>= count;
return ret;
}
@ -772,7 +773,7 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="huffmanTree">The huffman value</param>
/// <returns>The <see cref="byte" /></returns>
private byte DecodeHuffman(ref HuffmanTree huffmanTree)
internal byte DecodeHuffman(ref HuffmanTree huffmanTree)
{
// Copy stuff to the stack:
if (huffmanTree.Length == 0)
@ -780,20 +781,20 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Uninitialized Huffman table");
}
if (this.bits.UnreadBits < 8)
if (this.Bits.UnreadBits < 8)
{
ErrorCodes errorCode = this.bits.EnsureNBits(8, this);
ErrorCodes errorCode = this.Bits.EnsureNBits(8, this);
if (errorCode == ErrorCodes.NoError)
{
ushort v =
huffmanTree.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - HuffmanTree.LutSize)) & 0xff];
huffmanTree.Lut[(this.Bits.Accumulator >> (this.Bits.UnreadBits - HuffmanTree.LutSize)) & 0xff];
if (v != 0)
{
byte n = (byte)((v & 0xff) - 1);
this.bits.UnreadBits -= n;
this.bits.Mask >>= n;
this.Bits.UnreadBits -= n;
this.Bits.Mask >>= n;
return (byte)(v >> 8);
}
}
@ -806,22 +807,22 @@ namespace ImageSharp.Formats
int code = 0;
for (int i = 0; i < HuffmanTree.MaxCodeLength; i++)
{
if (this.bits.UnreadBits == 0)
if (this.Bits.UnreadBits == 0)
{
ErrorCodes errorCode = this.bits.EnsureNBits(1, this);
ErrorCodes 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)
{
code |= 1;
}
this.bits.UnreadBits--;
this.bits.Mask >>= 1;
this.Bits.UnreadBits--;
this.Bits.Mask >>= 1;
if (code <= huffmanTree.MaxCodes[i])
{
@ -834,7 +835,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Bad Huffman code");
}
private JpegPixelArea GetDestinationChannel(int compIndex)
internal JpegPixelArea GetDestinationChannel(int compIndex)
{
if (this.ComponentCount == 1)
{
@ -1089,7 +1090,7 @@ namespace ImageSharp.Formats
}
}
private void ProcessBlockImpl(
internal void ProcessBlockImpl(
int ah,
Block8x8F* b,
Block8x8F* temp1,
@ -1111,7 +1112,7 @@ namespace ImageSharp.Formats
int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector;
if (ah != 0)
{
this.Refine(b, ref this.huffmanTrees[huffmannIdx], unzigPtr, zigStart, zigEnd, 1 << al);
this.Refine(b, ref this.HuffmanTrees[huffmannIdx], unzigPtr, zigStart, zigEnd, 1 << al);
}
else
{
@ -1123,22 +1124,22 @@ namespace ImageSharp.Formats
// Decode the DC coefficient, as specified in section F.2.2.1.
byte value =
this.DecodeHuffman(
ref this.huffmanTrees[(DcTable * HuffmanTree.ThRowSize) + scan[i].DcTableSelector]);
ref this.HuffmanTrees[(DcTable * HuffmanTree.ThRowSize) + scan[i].DcTableSelector]);
if (value > 16)
{
throw new ImageFormatException("Excessive DC component");
}
int deltaDC = this.bits.ReceiveExtend(value, this);
int deltaDC = this.Bits.ReceiveExtend(value, this);
dc[compIndex] += deltaDC;
// b[0] = dc[compIndex] << al;
Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al);
}
if (zig <= zigEnd && this.eobRun > 0)
if (zig <= zigEnd && this.EobRun > 0)
{
this.eobRun--;
this.EobRun--;
}
else
{
@ -1146,7 +1147,7 @@ namespace ImageSharp.Formats
// Huffman huffv = ;
for (; zig <= zigEnd; zig++)
{
byte value = this.DecodeHuffman(ref this.huffmanTrees[huffmannIdx]);
byte value = this.DecodeHuffman(ref this.HuffmanTrees[huffmannIdx]);
byte val0 = (byte)(value >> 4);
byte val1 = (byte)(value & 0x0f);
if (val1 != 0)
@ -1157,7 +1158,7 @@ namespace ImageSharp.Formats
break;
}
int ac = this.bits.ReceiveExtend(val1, this);
int ac = this.Bits.ReceiveExtend(val1, this);
// b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al);
@ -1166,13 +1167,13 @@ namespace ImageSharp.Formats
{
if (val0 != 0x0f)
{
this.eobRun = (ushort)(1 << val0);
this.EobRun = (ushort)(1 << val0);
if (val0 != 0)
{
this.eobRun |= (ushort)this.DecodeBits(val0);
this.EobRun |= (ushort)this.DecodeBits(val0);
}
this.eobRun--;
this.EobRun--;
break;
}
@ -1276,7 +1277,7 @@ namespace ImageSharp.Formats
}
int huffTreeIndex = (tc * HuffmanTree.ThRowSize) + th;
this.huffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(this, this.Temp, ref remaining);
this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(this, this.Temp, ref remaining);
}
}
@ -1293,7 +1294,7 @@ namespace ImageSharp.Formats
}
this.ReadFull(this.Temp, 0, 2);
this.restartInterval = ((int)this.Temp[0] << 8) + (int)this.Temp[1];
this.RestartInterval = ((int)this.Temp[0] << 8) + (int)this.Temp[1];
}
/// <summary>
@ -1584,9 +1585,14 @@ namespace ImageSharp.Formats
private void ProcessStartOfScan22(int remaining)
{
DecoderScanProcessor processor = default(DecoderScanProcessor);
DecoderScanProcessor.Init(&processor, this, remaining);
this.MakeImage(processor.mxx, processor.myy);
Scan[] scan = new Scan[MaxComponents];
int[] dc = new int[MaxComponents];
DecoderScanProcessor p = default(DecoderScanProcessor);
DecoderScanProcessor.Init(&p, this, remaining, scan);
this.MakeImage(p.mxx, p.myy);
p.ProcessBlocks(this, scan, ref dc);
}
/// <summary>
@ -1699,7 +1705,7 @@ namespace ImageSharp.Formats
}
}
this.bits = default(Bits);
this.Bits = default(Bits);
int mcu = 0;
byte expectedRst = JpegConstants.Markers.RST0;
@ -1785,34 +1791,19 @@ namespace ImageSharp.Formats
// Load the previous partially decoded coefficients, if applicable.
if (this.IsProgressive)
{
this.blockIndex = ((@by * mxx) * hi) + bx;
fixed (Block8x8F* bp = &this.ProgCoeffs[compIndex][this.blockIndex])
int blockIndex = ((@by * mxx) * hi) + bx;
fixed (Block8x8F* bp = &this.ProgCoeffs[compIndex][blockIndex])
{
this.ProcessBlockImpl(
ah,
bp,
&temp1,
&temp2,
unzigPtr,
scan,
i,
zigStart,
zigEnd,
al,
dc,
compIndex,
@by,
mxx,
hi,
bx,
qtp);
Unsafe.CopyBlock(&b, bp, (uint) sizeof(Block8x8F) );
}
}
else
{
b.Clear();
this.ProcessBlockImpl(
}
this.ProcessBlockImpl(
ah,
&b,
&temp1,
@ -1830,7 +1821,6 @@ namespace ImageSharp.Formats
hi,
bx,
qtp);
}
}
}
@ -1840,7 +1830,7 @@ namespace ImageSharp.Formats
// for i
mcu++;
if (this.restartInterval > 0 && mcu % this.restartInterval == 0 && mcu < mxx * myy)
if (this.RestartInterval > 0 && mcu % this.RestartInterval == 0 && mcu < mxx * myy)
{
// 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.
@ -1857,13 +1847,13 @@ namespace ImageSharp.Formats
}
// Reset the Huffman decoder.
this.bits = default(Bits);
this.Bits = default(Bits);
// Reset the DC components, as per section F.2.1.3.1.
dc = new int[MaxComponents];
// Reset the progressive decoder state, as per section G.1.2.2.
this.eobRun = 0;
this.EobRun = 0;
}
}
@ -1909,7 +1899,7 @@ namespace ImageSharp.Formats
// Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3.
int zig = zigStart;
if (this.eobRun == 0)
if (this.EobRun == 0)
{
for (; zig <= zigEnd; zig++)
{
@ -1924,10 +1914,10 @@ namespace ImageSharp.Formats
case 0:
if (val0 != 0x0f)
{
this.eobRun = (ushort)(1 << val0);
this.EobRun = (ushort)(1 << val0);
if (val0 != 0)
{
this.eobRun |= (ushort)this.DecodeBits(val0);
this.EobRun |= (ushort)this.DecodeBits(val0);
}
done = true;
@ -1968,9 +1958,9 @@ namespace ImageSharp.Formats
}
}
if (this.eobRun > 0)
if (this.EobRun > 0)
{
this.eobRun--;
this.EobRun--;
this.RefineNonZeroes(b, zig, zigEnd, -1, delta, unzigPtr);
}
}
@ -2035,7 +2025,7 @@ namespace ImageSharp.Formats
// Unread the overshot bytes, if any.
if (this.bytes.UnreadableBytes != 0)
{
if (this.bits.UnreadBits >= 8)
if (this.Bits.UnreadBits >= 8)
{
this.UnreadByteStuffedByte();
}
@ -2073,11 +2063,11 @@ namespace ImageSharp.Formats
{
this.bytes.I -= this.bytes.UnreadableBytes;
this.bytes.UnreadableBytes = 0;
if (this.bits.UnreadBits >= 8)
if (this.Bits.UnreadBits >= 8)
{
this.bits.Accumulator >>= 8;
this.bits.UnreadBits -= 8;
this.bits.Mask >>= 8;
this.Bits.Accumulator >>= 8;
this.Bits.UnreadBits -= 8;
this.Bits.Mask >>= 8;
}
}

16
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

@ -428,5 +428,21 @@ namespace ImageSharp.Tests
Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f));
}
[Fact]
public unsafe void Copy_FromHeap()
{
Block8x8F[] blox = new Block8x8F[1];
blox[0].LoadFrom(Create8x8FloatData());
Block8x8F clone = default(Block8x8F);
fixed (Block8x8F* p = &blox[0])
{
Block8x8F.Copy(&clone, p);
}
Assert.Equal(blox[0], clone);
}
}
}
Loading…
Cancel
Save