Browse Source

almost worx

af/merge-core
antonfirsov 10 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 namespace ImageSharp.Formats.Jpg
{ {
using System; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
internal unsafe struct DecoderScanProcessor internal unsafe struct DecoderScanProcessor
{ {
[StructLayout(LayoutKind.Sequential)]
public struct ComponentData public struct ComponentData
{ {
public Block8x8F Block; public Block8x8F Block;
@ -12,11 +15,13 @@
public Block8x8F Temp2; public Block8x8F Temp2;
public Block8x8F QuantiazationTable;
public UnzigData Unzig; 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() public static ComponentData Create()
{ {
@ -24,7 +29,6 @@
data.Unzig = UnzigData.Create(); data.Unzig = UnzigData.Create();
return data; return data;
} }
} }
public struct ComponentPointers public struct ComponentPointers
@ -35,23 +39,31 @@
public Block8x8F* Temp2; public Block8x8F* Temp2;
public Block8x8F* QuantiazationTable;
public int* Unzig; public int* Unzig;
public Scan* Scan; //public Scan* Scan;
public int* Dc; //public int* Dc;
public ComponentPointers(ComponentData* basePtr) public ComponentPointers(ComponentData* basePtr)
{ {
this.Block = &basePtr->Block; this.Block = &basePtr->Block;
this.Temp1 = &basePtr->Temp1; this.Temp1 = &basePtr->Temp1;
this.Temp2 = &basePtr->Temp2; this.Temp2 = &basePtr->Temp2;
this.QuantiazationTable = &basePtr->QuantiazationTable;
this.Unzig = basePtr->Unzig.Data; this.Unzig = basePtr->Unzig.Data;
this.Scan = (Scan*) basePtr->ScanData; //this.Scan = (Scan*) basePtr->ScanData;
this.Dc = basePtr->Dc; //this.Dc = basePtr->Dc;
} }
} }
//private void ResetDc()
//{
// Unsafe.InitBlock(this.Pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents);
//}
public int bx; public int bx;
public int by; public int by;
@ -68,17 +80,20 @@
public int myy; public int myy;
public int scanComponentCount;
public ComponentData Data; public ComponentData Data;
public ComponentPointers Pointers; 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->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) if (decoder.ComponentCount == 0)
{ {
@ -91,9 +106,9 @@
} }
decoder.ReadFull(decoder.Temp, 0, remaining); 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) if (remaining != 4 + scanComponentCountX2)
{ {
throw new ImageFormatException("SOS length inconsistent with number of components"); throw new ImageFormatException("SOS length inconsistent with number of components");
@ -101,9 +116,9 @@
int totalHv = 0; 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 // 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. // total H*V values in a scan must be <= 10.
@ -127,7 +142,7 @@
throw new ImageFormatException("Bad spectral selection bounds"); 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"); throw new ImageFormatException("Progressive AC coefficients for more than one component");
} }
@ -146,12 +161,12 @@
if (decoder.IsProgressive) 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) 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.ComponentArray[compIndex].VerticalFactor;
decoder.ProgCoeffs[compIndex] = new Block8x8F[size]; 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. // Component selector.
int cs = decoder.Temp[1 + (2 * i)]; int cs = decoder.Temp[1 + (2 * i)];
@ -181,7 +196,7 @@
currentScan.Index = (byte)compIndex; 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, int i,
ref Scan currentScan, ref Scan currentScan,
ref int totalHv, 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 // 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 // the values of Cs_1 through Cs_(j-1)". Since we have previously
@ -199,7 +214,7 @@
// into comp are unique. // into comp are unique.
for (int j = 0; j < i; j++) 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"); 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;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using ImageSharp.Formats.Jpg; using ImageSharp.Formats.Jpg;
@ -19,12 +20,12 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// The AC table index /// The AC table index
/// </summary> /// </summary>
private const int AcTable = 1; internal const int AcTable = 1;
/// <summary> /// <summary>
/// The DC table index /// The DC table index
/// </summary> /// </summary>
private const int DcTable = 0; internal const int DcTable = 0;
/// <summary> /// <summary>
/// The maximum number of color components /// The maximum number of color components
@ -44,7 +45,7 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// The huffman trees /// The huffman trees
/// </summary> /// </summary>
private readonly HuffmanTree[] huffmanTrees; internal HuffmanTree[] HuffmanTrees { get; }
/// <summary> /// <summary>
/// Saved state between progressive-mode scans. /// Saved state between progressive-mode scans.
@ -76,11 +77,11 @@ namespace ImageSharp.Formats
/// <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;
private JpegPixelArea blackImage; private JpegPixelArea blackImage;
private int blockIndex; //private int blockIndex;
/// <summary> /// <summary>
/// The byte buffer. /// The byte buffer.
@ -95,7 +96,7 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// End-of-Band run, specified in section G.1.2.2. /// End-of-Band run, specified in section G.1.2.2.
/// </summary> /// </summary>
private ushort eobRun; internal ushort EobRun;
/// <summary> /// <summary>
/// A grayscale image to decode to. /// A grayscale image to decode to.
@ -135,7 +136,7 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// The restart interval /// The restart interval
/// </summary> /// </summary>
private int restartInterval; internal int RestartInterval { get; private set; }
/// <summary> /// <summary>
/// The vertical resolution. Calculated if the image has a JFIF header. /// The vertical resolution. Calculated if the image has a JFIF header.
@ -152,15 +153,15 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
public JpegDecoderCore() public JpegDecoderCore()
{ {
this.huffmanTrees = HuffmanTree.CreateHuffmanTrees(); this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees();
this.QuantizationTables = new Block8x8F[MaxTq + 1]; this.QuantizationTables = new Block8x8F[MaxTq + 1];
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.ProgCoeffs = new Block8x8F[MaxComponents][]; this.ProgCoeffs = new Block8x8F[MaxComponents][];
this.bits = default(Bits); this.Bits = default(Bits);
this.bytes = Bytes.Create(); this.bytes = Bytes.Create();
} }
/// <summary> /// <summary>
/// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent) /// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent)
/// It's better tho have an error code for this! /// It's better tho have an error code for this!
@ -333,7 +334,7 @@ namespace ImageSharp.Formats
return; return;
} }
this.ProcessStartOfScan(remaining); this.ProcessStartOfScan22(remaining);
break; break;
case JpegConstants.Markers.DRI: case JpegConstants.Markers.DRI:
if (configOnly) if (configOnly)
@ -430,9 +431,9 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
public void Dispose() 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(); this.ycbcrImage?.Dispose();
@ -440,7 +441,7 @@ namespace ImageSharp.Formats
this.grayImage.ReturnPooled(); this.grayImage.ReturnPooled();
this.blackImage.ReturnPooled(); this.blackImage.ReturnPooled();
} }
/// <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>
@ -462,7 +463,7 @@ namespace ImageSharp.Formats
// Unread the overshot bytes, if any. // Unread the overshot bytes, if any.
if (this.bytes.UnreadableBytes != 0) if (this.bytes.UnreadableBytes != 0)
{ {
if (this.bits.UnreadBits >= 8) if (this.Bits.UnreadBits >= 8)
{ {
this.UnreadByteStuffedByte(); this.UnreadByteStuffedByte();
} }
@ -727,20 +728,20 @@ namespace ImageSharp.Formats
/// Decodes a single bit /// Decodes a single bit
/// </summary> /// </summary>
/// <returns>The <see cref="bool" /></returns> /// <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) if (errorCode != ErrorCodes.NoError)
{ {
throw new MissingFF00Exception(); throw new MissingFF00Exception();
} }
} }
bool ret = (this.bits.Accumulator & this.bits.Mask) != 0; bool ret = (this.Bits.Accumulator & this.Bits.Mask) != 0;
this.bits.UnreadBits--; this.Bits.UnreadBits--;
this.bits.Mask >>= 1; this.Bits.Mask >>= 1;
return ret; return ret;
} }
@ -749,21 +750,21 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <param name="count">The number of bits to decode.</param> /// <param name="count">The number of bits to decode.</param>
/// <returns>The <see cref="uint" /></returns> /// <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) if (errorCode != ErrorCodes.NoError)
{ {
throw new MissingFF00Exception(); 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)); ret = (uint)(ret & ((1 << count) - 1));
this.bits.UnreadBits -= count; this.Bits.UnreadBits -= count;
this.bits.Mask >>= count; this.Bits.Mask >>= count;
return ret; return ret;
} }
@ -772,7 +773,7 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <param name="huffmanTree">The huffman value</param> /// <param name="huffmanTree">The huffman value</param>
/// <returns>The <see cref="byte" /></returns> /// <returns>The <see cref="byte" /></returns>
private byte DecodeHuffman(ref HuffmanTree huffmanTree) internal byte DecodeHuffman(ref HuffmanTree huffmanTree)
{ {
// Copy stuff to the stack: // Copy stuff to the stack:
if (huffmanTree.Length == 0) if (huffmanTree.Length == 0)
@ -780,20 +781,20 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Uninitialized Huffman table"); 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) if (errorCode == ErrorCodes.NoError)
{ {
ushort v = 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) if (v != 0)
{ {
byte n = (byte)((v & 0xff) - 1); byte n = (byte)((v & 0xff) - 1);
this.bits.UnreadBits -= n; this.Bits.UnreadBits -= n;
this.bits.Mask >>= n; this.Bits.Mask >>= n;
return (byte)(v >> 8); return (byte)(v >> 8);
} }
} }
@ -806,22 +807,22 @@ namespace ImageSharp.Formats
int code = 0; int code = 0;
for (int i = 0; i < HuffmanTree.MaxCodeLength; i++) 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) if (errorCode != ErrorCodes.NoError)
{ {
throw new MissingFF00Exception(); throw new MissingFF00Exception();
} }
} }
if ((this.bits.Accumulator & this.bits.Mask) != 0) if ((this.Bits.Accumulator & this.Bits.Mask) != 0)
{ {
code |= 1; code |= 1;
} }
this.bits.UnreadBits--; this.Bits.UnreadBits--;
this.bits.Mask >>= 1; this.Bits.Mask >>= 1;
if (code <= huffmanTree.MaxCodes[i]) if (code <= huffmanTree.MaxCodes[i])
{ {
@ -834,7 +835,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Bad Huffman code"); throw new ImageFormatException("Bad Huffman code");
} }
private JpegPixelArea GetDestinationChannel(int compIndex) internal JpegPixelArea GetDestinationChannel(int compIndex)
{ {
if (this.ComponentCount == 1) if (this.ComponentCount == 1)
{ {
@ -1089,7 +1090,7 @@ namespace ImageSharp.Formats
} }
} }
private void ProcessBlockImpl( internal void ProcessBlockImpl(
int ah, int ah,
Block8x8F* b, Block8x8F* b,
Block8x8F* temp1, Block8x8F* temp1,
@ -1111,7 +1112,7 @@ namespace ImageSharp.Formats
int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector; int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector;
if (ah != 0) 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 else
{ {
@ -1123,22 +1124,22 @@ namespace ImageSharp.Formats
// Decode the DC coefficient, as specified in section F.2.2.1. // Decode the DC coefficient, as specified in section F.2.2.1.
byte value = byte value =
this.DecodeHuffman( this.DecodeHuffman(
ref this.huffmanTrees[(DcTable * HuffmanTree.ThRowSize) + scan[i].DcTableSelector]); ref this.HuffmanTrees[(DcTable * HuffmanTree.ThRowSize) + scan[i].DcTableSelector]);
if (value > 16) if (value > 16)
{ {
throw new ImageFormatException("Excessive DC component"); throw new ImageFormatException("Excessive DC component");
} }
int deltaDC = this.bits.ReceiveExtend(value, this); int deltaDC = this.Bits.ReceiveExtend(value, this);
dc[compIndex] += deltaDC; dc[compIndex] += deltaDC;
// b[0] = dc[compIndex] << al; // b[0] = dc[compIndex] << al;
Block8x8F.SetScalarAt(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 else
{ {
@ -1146,7 +1147,7 @@ namespace ImageSharp.Formats
// Huffman huffv = ; // Huffman huffv = ;
for (; zig <= zigEnd; zig++) 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 val0 = (byte)(value >> 4);
byte val1 = (byte)(value & 0x0f); byte val1 = (byte)(value & 0x0f);
if (val1 != 0) if (val1 != 0)
@ -1157,7 +1158,7 @@ namespace ImageSharp.Formats
break; break;
} }
int ac = this.bits.ReceiveExtend(val1, this); int ac = this.Bits.ReceiveExtend(val1, this);
// b[Unzig[zig]] = ac << al; // b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al); Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al);
@ -1166,13 +1167,13 @@ namespace ImageSharp.Formats
{ {
if (val0 != 0x0f) if (val0 != 0x0f)
{ {
this.eobRun = (ushort)(1 << val0); this.EobRun = (ushort)(1 << val0);
if (val0 != 0) if (val0 != 0)
{ {
this.eobRun |= (ushort)this.DecodeBits(val0); this.EobRun |= (ushort)this.DecodeBits(val0);
} }
this.eobRun--; this.EobRun--;
break; break;
} }
@ -1276,7 +1277,7 @@ 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(this, this.Temp, ref remaining);
} }
} }
@ -1293,7 +1294,7 @@ namespace ImageSharp.Formats
} }
this.ReadFull(this.Temp, 0, 2); 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> /// <summary>
@ -1584,9 +1585,14 @@ namespace ImageSharp.Formats
private void ProcessStartOfScan22(int remaining) private void ProcessStartOfScan22(int remaining)
{ {
DecoderScanProcessor processor = default(DecoderScanProcessor); Scan[] scan = new Scan[MaxComponents];
DecoderScanProcessor.Init(&processor, this, remaining); int[] dc = new int[MaxComponents];
this.MakeImage(processor.mxx, processor.myy);
DecoderScanProcessor p = default(DecoderScanProcessor);
DecoderScanProcessor.Init(&p, this, remaining, scan);
this.MakeImage(p.mxx, p.myy);
p.ProcessBlocks(this, scan, ref dc);
} }
/// <summary> /// <summary>
@ -1699,7 +1705,7 @@ namespace ImageSharp.Formats
} }
} }
this.bits = default(Bits); this.Bits = default(Bits);
int mcu = 0; int mcu = 0;
byte expectedRst = JpegConstants.Markers.RST0; byte expectedRst = JpegConstants.Markers.RST0;
@ -1785,34 +1791,19 @@ namespace ImageSharp.Formats
// Load the previous partially decoded coefficients, if applicable. // Load the previous partially decoded coefficients, if applicable.
if (this.IsProgressive) if (this.IsProgressive)
{ {
this.blockIndex = ((@by * mxx) * hi) + bx; int blockIndex = ((@by * mxx) * hi) + bx;
fixed (Block8x8F* bp = &this.ProgCoeffs[compIndex][this.blockIndex]) fixed (Block8x8F* bp = &this.ProgCoeffs[compIndex][blockIndex])
{ {
this.ProcessBlockImpl( Unsafe.CopyBlock(&b, bp, (uint) sizeof(Block8x8F) );
ah,
bp,
&temp1,
&temp2,
unzigPtr,
scan,
i,
zigStart,
zigEnd,
al,
dc,
compIndex,
@by,
mxx,
hi,
bx,
qtp);
} }
} }
else else
{ {
b.Clear(); b.Clear();
this.ProcessBlockImpl( }
this.ProcessBlockImpl(
ah, ah,
&b, &b,
&temp1, &temp1,
@ -1830,7 +1821,6 @@ namespace ImageSharp.Formats
hi, hi,
bx, bx,
qtp); qtp);
}
} }
} }
@ -1840,7 +1830,7 @@ namespace ImageSharp.Formats
// for i // for i
mcu++; 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, // 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.
@ -1857,13 +1847,13 @@ namespace ImageSharp.Formats
} }
// Reset the Huffman decoder. // Reset the Huffman decoder.
this.bits = default(Bits); this.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.
dc = new int[MaxComponents]; dc = new int[MaxComponents];
// Reset the progressive decoder state, as per section G.1.2.2. // 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. // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3.
int zig = zigStart; int zig = zigStart;
if (this.eobRun == 0) if (this.EobRun == 0)
{ {
for (; zig <= zigEnd; zig++) for (; zig <= zigEnd; zig++)
{ {
@ -1924,10 +1914,10 @@ namespace ImageSharp.Formats
case 0: case 0:
if (val0 != 0x0f) if (val0 != 0x0f)
{ {
this.eobRun = (ushort)(1 << val0); this.EobRun = (ushort)(1 << val0);
if (val0 != 0) if (val0 != 0)
{ {
this.eobRun |= (ushort)this.DecodeBits(val0); this.EobRun |= (ushort)this.DecodeBits(val0);
} }
done = true; 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); this.RefineNonZeroes(b, zig, zigEnd, -1, delta, unzigPtr);
} }
} }
@ -2035,7 +2025,7 @@ namespace ImageSharp.Formats
// Unread the overshot bytes, if any. // Unread the overshot bytes, if any.
if (this.bytes.UnreadableBytes != 0) if (this.bytes.UnreadableBytes != 0)
{ {
if (this.bits.UnreadBits >= 8) if (this.Bits.UnreadBits >= 8)
{ {
this.UnreadByteStuffedByte(); this.UnreadByteStuffedByte();
} }
@ -2073,11 +2063,11 @@ namespace ImageSharp.Formats
{ {
this.bytes.I -= this.bytes.UnreadableBytes; this.bytes.I -= this.bytes.UnreadableBytes;
this.bytes.UnreadableBytes = 0; this.bytes.UnreadableBytes = 0;
if (this.bits.UnreadBits >= 8) if (this.Bits.UnreadBits >= 8)
{ {
this.bits.Accumulator >>= 8; this.Bits.Accumulator >>= 8;
this.bits.UnreadBits -= 8; this.Bits.UnreadBits -= 8;
this.bits.Mask >>= 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)); 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