Browse Source

JpegScanDecoder replaced the old stuff!

pull/62/head
antonfirsov 9 years ago
parent
commit
a54e213c97
  1. 171
      src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs
  2. 625
      src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs

171
src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs → src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs

@ -1,11 +1,22 @@
namespace ImageSharp.Formats.Jpg
// ReSharper disable InconsistentNaming
namespace ImageSharp.Formats.Jpg
{
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
internal unsafe struct DecoderScanProcessor
internal unsafe struct JpegScanDecoder
{
/// <summary>
/// The AC table index
/// </summary>
internal const int AcTableIndex = 1;
/// <summary>
/// The DC table index
/// </summary>
internal const int DcTableIndex = 0;
[StructLayout(LayoutKind.Sequential)]
public struct ComponentData
{
@ -19,9 +30,9 @@
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()
{
@ -43,9 +54,9 @@
public int* Unzig;
//public Scan* Scan;
public Scan* Scan;
//public int* Dc;
public int* Dc;
public ComponentPointers(ComponentData* basePtr)
{
@ -54,46 +65,67 @@
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);
//}
private void ResetDc()
{
Unsafe.InitBlock(this.Pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents);
}
// bx and by are the location of the current block, in units of 8x8
// blocks: the third block in the first row has (bx, by) = (2, 0).
private int bx;
private int by;
public int bx;
// zigStart and zigEnd are the spectral selection bounds.
// ah and al are the successive approximation high and low values.
// The spec calls these values Ss, Se, Ah and Al.
// For progressive JPEGs, these are the two more-or-less independent
// aspects of progression. Spectral selection progression is when not
// all of a block's 64 DCT coefficients are transmitted in one pass.
// For example, three passes could transmit coefficient 0 (the DC
// component), coefficients 1-5, and coefficients 6-63, in zig-zag
// order. Successive approximation is when not all of the bits of a
// band of coefficients are transmitted in one pass. For example,
// three passes could transmit the 6 most significant bits, followed
// by the second-least significant bit, followed by the least
// significant bit.
// For baseline JPEGs, these parameters are hard-coded to 0/63/0/0.
public int by;
private int zigStart;
public int zigStart;
private int zigEnd;
public int zigEnd;
private int ah;
public int ah;
private int al;
public int al;
// XNumberOfMCUs and YNumberOfMCUs are the number of MCUs (Minimum Coded Units) in the image.
public int mxx;
public int XNumberOfMCUs;
public int myy;
public int YNumberOfMCUs;
public int scanComponentCount;
private int scanComponentCount;
public ComponentData Data;
private ComponentData Data;
public ComponentPointers Pointers;
private ComponentPointers Pointers;
public static void Init(DecoderScanProcessor* p, JpegDecoderCore decoder, int remaining, Scan[] scan)
public static void Init(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining)
{
p->Data = ComponentData.Create();
p->Pointers = new ComponentPointers(&p->Data);
p->InitImpl(decoder, remaining, scan);
p->InitImpl(decoder, remaining);
}
private void InitImpl(JpegDecoderCore decoder, int remaining, Scan[] scan)
private void InitImpl(JpegDecoderCore decoder, int remaining)
{
if (decoder.ComponentCount == 0)
{
@ -118,7 +150,7 @@
for (int i = 0; i < this.scanComponentCount; i++)
{
this.ProcessScanImpl(decoder, i, ref scan[i], ref totalHv, scan);
this.ProcessScanImpl(decoder, i, ref this.Pointers.Scan[i], ref totalHv);
}
// 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.
@ -153,20 +185,20 @@
}
}
// mxx and myy are the number of MCUs (Minimum Coded Units) in the image.
// XNumberOfMCUs and YNumberOfMCUs are the number of MCUs (Minimum Coded Units) in the image.
int h0 = decoder.ComponentArray[0].HorizontalFactor;
int v0 = decoder.ComponentArray[0].VerticalFactor;
this.mxx = (decoder.ImageWidth + (8 * h0) - 1) / (8 * h0);
this.myy = (decoder.ImageHeight + (8 * v0) - 1) / (8 * v0);
this.XNumberOfMCUs = (decoder.ImageWidth + (8 * h0) - 1) / (8 * h0);
this.YNumberOfMCUs = (decoder.ImageHeight + (8 * v0) - 1) / (8 * v0);
if (decoder.IsProgressive)
{
for (int i = 0; i < this.scanComponentCount; i++)
{
int compIndex = scan[i].Index;
int compIndex = this.Pointers.Scan[i].Index;
if (decoder.ProgCoeffs[compIndex] == null)
{
int size = this.mxx * this.myy * decoder.ComponentArray[compIndex].HorizontalFactor
int size = this.XNumberOfMCUs * this.YNumberOfMCUs * decoder.ComponentArray[compIndex].HorizontalFactor
* decoder.ComponentArray[compIndex].VerticalFactor;
decoder.ProgCoeffs[compIndex] = new Block8x8F[size];
@ -175,7 +207,7 @@
}
}
private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv, Scan[] scan)
private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv)
{
// Component selector.
int cs = decoder.Temp[1 + (2 * i)];
@ -196,7 +228,7 @@
currentScan.Index = (byte)compIndex;
this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex], scan);
this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex]);
}
@ -205,7 +237,7 @@
int i,
ref Scan currentScan,
ref int totalHv,
ref Component currentComponent, Scan[] scan)
ref Component currentComponent)
{
// 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
@ -214,7 +246,7 @@
// into comp are unique.
for (int j = 0; j < i; j++)
{
if (currentScan.Index == scan[j].Index)
if (currentScan.Index == this.Pointers.Scan[j].Index)
{
throw new ImageFormatException("Repeated component selector");
}
@ -235,19 +267,19 @@
}
}
public void ProcessBlocks(JpegDecoderCore decoder, Scan[] scan, ref int[] dc)
public void ProcessBlocks(JpegDecoderCore decoder)
{
int blockCount = 0;
int mcu = 0;
byte expectedRst = JpegConstants.Markers.RST0;
for (int my = 0; my < this.myy; my++)
for (int my = 0; my < this.YNumberOfMCUs; my++)
{
for (int mx = 0; mx < this.mxx; mx++)
for (int mx = 0; mx < this.XNumberOfMCUs; mx++)
{
for (int i = 0; i < this.scanComponentCount; i++)
{
int compIndex = scan[i].Index;
int compIndex = this.Pointers.Scan[i].Index;
int hi = decoder.ComponentArray[compIndex].HorizontalFactor;
int vi = decoder.ComponentArray[compIndex].VerticalFactor;
@ -282,7 +314,7 @@
}
else
{
int q = this.mxx * hi;
int q = this.XNumberOfMCUs * hi;
this.bx = blockCount % q;
this.by = blockCount / q;
blockCount++;
@ -301,7 +333,7 @@
//Load the previous partially decoded coefficients, if applicable.
if (decoder.IsProgressive)
{
int blockIndex = ((this.by * this.mxx) * hi) + this.bx;
int blockIndex = ((this.by * this.XNumberOfMCUs) * hi) + this.bx;
this.Data.Block = decoder.ProgCoeffs[compIndex][blockIndex];
}
else
@ -309,7 +341,7 @@
this.Data.Block.Clear();
}
this.ProcessBlockImpl(decoder, i, compIndex, hi, scan, ref dc);
this.ProcessBlockImpl(decoder, i, compIndex, hi);
}
// for j
@ -318,7 +350,7 @@
// for i
mcu++;
if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < this.mxx * this.myy)
if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < this.XNumberOfMCUs * this.YNumberOfMCUs)
{
// 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.
@ -338,9 +370,8 @@
decoder.Bits = default(Bits);
// Reset the DC components, as per section F.2.1.3.1.
//this.ResetDc();
dc = new int[JpegDecoderCore.MaxComponents];
this.ResetDc();
// Reset the progressive decoder state, as per section G.1.2.2.
decoder.EobRun = 0;
}
@ -351,15 +382,11 @@
}
/// <summary>
/// The AC table index
/// Decodes a successive approximation refinement block, as specified in section G.1.2.
/// </summary>
internal const int AcTable = 1;
/// <summary>
/// The DC table index
/// </summary>
internal const int DcTable = 0;
/// <param name="h">The Huffman tree</param>
/// <param name="delta">The low transform offset</param>
/// <param name="decoder">The decoder instance</param>
private void Refine(JpegDecoderCore decoder, ref HuffmanTree h, int delta)
{
Block8x8F* b = this.Pointers.Block;
@ -430,9 +457,7 @@
{
break;
}
int blah = zig;
zig = this.RefineNonZeroes(decoder, zig, val0, delta);
if (zig > this.zigEnd)
{
@ -453,11 +478,20 @@
this.RefineNonZeroes(decoder, zig,-1, delta);
}
}
/// <summary>
/// Refines non-zero entries of b in zig-zag order.
/// If <paramref name="nz" /> >= 0, the first <paramref name="nz" /> zero entries are skipped over.
/// </summary>
/// <param name="decoder">The decoder</param>
/// <param name="zig">The zig-zag start index</param>
/// <param name="nz">The non-zero entry</param>
/// <param name="delta">The low transform offset</param>
/// <returns>The <see cref="int" /></returns>
private int RefineNonZeroes(JpegDecoderCore decoder, int zig, int nz, int delta)
{
var b = this.Pointers.Block;
for (; zig <= zigEnd; zig++)
for (; zig <= this.zigEnd; zig++)
{
int u = this.Pointers.Unzig[zig];
float bu = Block8x8F.GetScalarAt(b, u);
@ -495,18 +529,18 @@
return zig;
}
private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi, Scan[] scan, ref int[] dc)
private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi)
{
var b = this.Pointers.Block;
//var dc = this.Pointers.Dc;
int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector;
int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.Pointers.Scan[i].AcTableSelector;
if (this.ah != 0)
{
this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al);
}
else
{
int zig = zigStart;
int zig = this.zigStart;
if (zig == 0)
{
zig++;
@ -514,17 +548,17 @@
// 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]);
ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.Pointers.Scan[i].DcTableSelector]);
if (value > 16)
{
throw new ImageFormatException("Excessive DC component");
}
int deltaDC = decoder.Bits.ReceiveExtend(value, decoder);
dc[compIndex] += deltaDC;
this.Pointers.Dc[compIndex] += deltaDC;
// b[0] = dc[compIndex] << al;
Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al);
Block8x8F.SetScalarAt(b, 0, this.Pointers.Dc[compIndex] << al);
}
if (zig <= this.zigEnd && decoder.EobRun > 0)
@ -534,7 +568,6 @@
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]);
@ -578,8 +611,8 @@
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;
// this.ProgCoeffs[compIndex][((@by * XNumberOfMCUs) * hi) + bx] = b.Clone();
decoder.ProgCoeffs[compIndex][((this.by * this.XNumberOfMCUs) * 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

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

@ -17,16 +17,6 @@ namespace ImageSharp.Formats
/// </summary>
internal unsafe class JpegDecoderCore : IDisposable
{
/// <summary>
/// The AC table index
/// </summary>
internal const int AcTable = 1;
/// <summary>
/// The DC table index
/// </summary>
internal const int DcTable = 0;
/// <summary>
/// The maximum number of color components
/// </summary>
@ -334,7 +324,7 @@ namespace ImageSharp.Formats
return;
}
this.ProcessStartOfScan22(remaining);
this.ProcessStartOfScan(remaining);
break;
case JpegConstants.Markers.DRI:
if (configOnly)
@ -1090,164 +1080,6 @@ namespace ImageSharp.Formats
}
}
internal void ProcessBlockImpl(
int ah,
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)
{
int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector;
if (ah != 0)
{
this.Refine(b, ref this.HuffmanTrees[huffmannIdx], unzigPtr, zigStart, zigEnd, 1 << al);
}
else
{
int zig = zigStart;
if (zig == 0)
{
zig++;
// 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]);
if (value > 16)
{
throw new ImageFormatException("Excessive DC component");
}
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)
{
this.EobRun--;
}
else
{
// Decode the AC coefficients, as specified in section F.2.2.2.
// Huffman huffv = ;
for (; zig <= zigEnd; zig++)
{
byte value = this.DecodeHuffman(ref this.HuffmanTrees[huffmannIdx]);
byte val0 = (byte)(value >> 4);
byte val1 = (byte)(value & 0x0f);
if (val1 != 0)
{
zig += val0;
if (zig > zigEnd)
{
break;
}
int ac = this.Bits.ReceiveExtend(val1, this);
// b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al);
}
else
{
if (val0 != 0x0f)
{
this.EobRun = (ushort)(1 << val0);
if (val0 != 0)
{
this.EobRun |= (ushort)this.DecodeBits(val0);
}
this.EobRun--;
break;
}
zig += 0x0f;
}
}
}
}
if (this.IsProgressive)
{
if (zigEnd != Block8x8F.ScalarCount - 1 || al != 0)
{
// We haven't completely decoded this 8x8 block. Save the coefficients.
// TODO!!!
// throw new NotImplementedException();
// this.ProgCoeffs[compIndex][((@by * mxx) * hi) + bx] = b.Clone();
this.ProgCoeffs[compIndex][((@by * mxx) * hi) + 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, qt, unzigPtr);
DCT.TransformIDCT(ref *b, ref *temp1, ref *temp2);
var destChannel = this.GetDestinationChannel(compIndex);
var destArea = destChannel.GetOffsetedSubAreaForBlock(bx, by);
destArea.LoadColorsFrom(temp1, temp2);
}
private void ProcessComponentImpl(
int i,
ref Scan currentScan,
Scan[] scan,
ref int totalHv,
ref Component currentComponent)
{
// 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
// verified that a frame's component identifiers (C_i values in section
// B.2.2) are unique, it suffices to check that the implicit indexes
// into comp are unique.
for (int j = 0; j < i; j++)
{
if (currentScan.Index == scan[j].Index)
{
throw new ImageFormatException("Repeated component selector");
}
}
totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor;
currentScan.DcTableSelector = (byte)(this.Temp[2 + (2 * i)] >> 4);
if (currentScan.DcTableSelector > HuffmanTree.MaxTh)
{
throw new ImageFormatException("Bad DC table selector value");
}
currentScan.AcTableSelector = (byte)(this.Temp[2 + (2 * i)] & 0x0f);
if (currentScan.AcTableSelector > HuffmanTree.MaxTh)
{
throw new ImageFormatException("Bad AC table selector value");
}
}
/// <summary>
/// Processes a Define Huffman Table marker, and initializes a huffman
/// struct from its contents. Specified in section B.2.4.2.
@ -1368,30 +1200,6 @@ namespace ImageSharp.Formats
}
}
private void ProcessScanImpl(int i, ref Scan currentScan, Scan[] scan, ref int totalHv)
{
// Component selector.
int cs = this.Temp[1 + (2 * i)];
int compIndex = -1;
for (int j = 0; j < this.ComponentCount; j++)
{
// Component compv = ;
if (cs == this.ComponentArray[j].Identifier)
{
compIndex = j;
}
}
if (compIndex < 0)
{
throw new ImageFormatException("Unknown component selector");
}
currentScan.Index = (byte)compIndex;
this.ProcessComponentImpl(i, ref currentScan, scan, ref totalHv, ref this.ComponentArray[compIndex]);
}
/// <summary>
/// Processes the Start of Frame marker. Specified in section B.2.2.
/// </summary>
@ -1583,438 +1391,21 @@ namespace ImageSharp.Formats
}
}
private void ProcessStartOfScan22(int remaining)
{
Scan[] scan = new Scan[MaxComponents];
int[] dc = new int[MaxComponents];
DecoderScanProcessor p = default(DecoderScanProcessor);
DecoderScanProcessor.Init(&p, this, remaining, scan);
this.Bits = default(Bits);
this.MakeImage(p.mxx, p.myy);
p.ProcessBlocks(this, scan, ref dc);
}
/// <summary>
/// Processes the SOS (Start of scan marker).
/// Processes the SOS (Start of scan marker).
/// </summary>
/// <remarks>
/// TODO: This also needs some significant refactoring to follow a more OO format.
/// </remarks>
/// <param name="remaining">The remaining bytes in the segment block.</param>
/// <exception cref="ImageFormatException">
/// Missing SOF Marker
/// SOS has wrong length
/// Missing SOF Marker
/// SOS has wrong length
/// </exception>
private void ProcessStartOfScan(int remaining)
{
if (this.ComponentCount == 0)
{
throw new ImageFormatException("Missing SOF marker");
}
if (remaining < 6 || 4 + (2 * this.ComponentCount) < remaining || remaining % 2 != 0)
{
throw new ImageFormatException("SOS has wrong length");
}
this.ReadFull(this.Temp, 0, remaining);
byte scanComponentCount = this.Temp[0];
int scanComponentCountX2 = 2 * scanComponentCount;
if (remaining != 4 + scanComponentCountX2)
{
throw new ImageFormatException("SOS length inconsistent with number of components");
}
Scan[] scan = new Scan[MaxComponents];
int totalHv = 0;
for (int i = 0; i < scanComponentCount; i++)
{
this.ProcessScanImpl(i, ref scan[i], scan, ref totalHv);
}
// 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.
if (this.ComponentCount > 1 && totalHv > 10)
{
throw new ImageFormatException("Total sampling factors too large.");
}
// zigStart and zigEnd are the spectral selection bounds.
// ah and al are the successive approximation high and low values.
// The spec calls these values Ss, Se, Ah and Al.
// For progressive JPEGs, these are the two more-or-less independent
// aspects of progression. Spectral selection progression is when not
// all of a block's 64 DCT coefficients are transmitted in one pass.
// For example, three passes could transmit coefficient 0 (the DC
// component), coefficients 1-5, and coefficients 6-63, in zig-zag
// order. Successive approximation is when not all of the bits of a
// band of coefficients are transmitted in one pass. For example,
// three passes could transmit the 6 most significant bits, followed
// by the second-least significant bit, followed by the least
// significant bit.
// For baseline JPEGs, these parameters are hard-coded to 0/63/0/0.
int zigStart = 0;
int zigEnd = Block8x8F.ScalarCount - 1;
int ah = 0;
int al = 0;
if (this.IsProgressive)
{
zigStart = this.Temp[1 + scanComponentCountX2];
zigEnd = this.Temp[2 + scanComponentCountX2];
ah = this.Temp[3 + scanComponentCountX2] >> 4;
al = this.Temp[3 + scanComponentCountX2] & 0x0f;
if ((zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || zigEnd >= Block8x8F.ScalarCount)
{
throw new ImageFormatException("Bad spectral selection bounds");
}
if (zigStart != 0 && scanComponentCount != 1)
{
throw new ImageFormatException("Progressive AC coefficients for more than one component");
}
if (ah != 0 && ah != al + 1)
{
throw new ImageFormatException("Bad successive approximation values");
}
}
// mxx and myy are the number of MCUs (Minimum Coded Units) in the image.
int h0 = this.ComponentArray[0].HorizontalFactor;
int v0 = this.ComponentArray[0].VerticalFactor;
int mxx = (this.ImageWidth + (8 * h0) - 1) / (8 * h0);
int myy = (this.ImageHeight + (8 * v0) - 1) / (8 * v0);
if (this.IsProgressive)
{
for (int i = 0; i < scanComponentCount; i++)
{
int compIndex = scan[i].Index;
if (this.ProgCoeffs[compIndex] == null)
{
int size = mxx * myy * this.ComponentArray[compIndex].HorizontalFactor
* this.ComponentArray[compIndex].VerticalFactor;
this.ProgCoeffs[compIndex] = new Block8x8F[size];
}
}
}
JpegScanDecoder scan = default(JpegScanDecoder);
JpegScanDecoder.Init(&scan, this, remaining);
this.Bits = default(Bits);
int mcu = 0;
byte expectedRst = JpegConstants.Markers.RST0;
// b is the decoded coefficients block, in natural (not zig-zag) order.
// Block b;
int[] dc = new int[MaxComponents];
// bx and by are the location of the current block, in units of 8x8
// blocks: the third block in the first row has (bx, by) = (2, 0).
int bx, by, blockCount = 0;
// TODO: A DecoderScanProcessor struct could clean up this mess
Block8x8F b = default(Block8x8F);
Block8x8F temp1 = default(Block8x8F);
Block8x8F temp2 = default(Block8x8F);
UnzigData unzig = UnzigData.Create();
int* unzigPtr = unzig.Data;
this.MakeImage(mxx, myy);
for (int my = 0; my < myy; my++)
{
for (int mx = 0; mx < mxx; mx++)
{
for (int i = 0; i < scanComponentCount; i++)
{
int compIndex = scan[i].Index;
int hi = this.ComponentArray[compIndex].HorizontalFactor;
int vi = this.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 (scanComponentCount != 1)
{
bx = (hi * mx) + (j % hi);
by = (vi * my) + (j / hi);
}
else
{
int q = mxx * hi;
bx = blockCount % q;
by = blockCount / q;
blockCount++;
if (bx * 8 >= this.ImageWidth || by * 8 >= this.ImageHeight)
{
continue;
}
}
int qtIndex = this.ComponentArray[compIndex].Selector;
// TODO: A DecoderScanProcessor struct could clean up this mess
// TODO: Reading & processing blocks should be done in 2 separate loops. The second one could be parallelized. The first one could be async.
fixed (Block8x8F* qtp = &this.QuantizationTables[qtIndex])
{
// Load the previous partially decoded coefficients, if applicable.
if (this.IsProgressive)
{
int blockIndex = ((@by * mxx) * hi) + bx;
fixed (Block8x8F* bp = &this.ProgCoeffs[compIndex][blockIndex])
{
Unsafe.CopyBlock(&b, bp, (uint) sizeof(Block8x8F) );
}
}
else
{
b.Clear();
}
this.ProcessBlockImpl(
ah,
&b,
&temp1,
&temp2,
unzigPtr,
scan,
i,
zigStart,
zigEnd,
al,
dc,
compIndex,
@by,
mxx,
hi,
bx,
qtp);
}
}
// for j
}
// for i
mcu++;
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.
this.ReadFull(this.Temp, 0, 2);
if (this.Temp[0] != 0xff || this.Temp[1] != expectedRst)
{
throw new ImageFormatException("Bad RST marker");
}
expectedRst++;
if (expectedRst == JpegConstants.Markers.RST7 + 1)
{
expectedRst = JpegConstants.Markers.RST0;
}
// Reset the Huffman decoder.
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;
}
}
// for mx
}
// for my
}
/// <summary>
/// Decodes a successive approximation refinement block, as specified in section G.1.2.
/// </summary>
/// <param name="b">The block of coefficients</param>
/// <param name="h">The Huffman tree</param>
/// <param name="unzigPtr">Unzig ptr</param>
/// <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 HuffmanTree h, int* unzigPtr, int zigStart, int zigEnd, int delta)
{
// Refining a DC component is trivial.
if (zigStart == 0)
{
if (zigEnd != 0)
{
throw new ImageFormatException("Invalid state for zig DC component");
}
bool bit = this.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 = zigStart;
if (this.EobRun == 0)
{
for (; zig <= zigEnd; zig++)
{
bool done = false;
int z = 0;
byte val = this.DecodeHuffman(ref h);
int val0 = val >> 4;
int val1 = val & 0x0f;
switch (val1)
{
case 0:
if (val0 != 0x0f)
{
this.EobRun = (ushort)(1 << val0);
if (val0 != 0)
{
this.EobRun |= (ushort)this.DecodeBits(val0);
}
done = true;
}
break;
case 1:
z = delta;
bool bit = this.DecodeBit();
if (!bit)
{
z = -z;
}
break;
default:
throw new ImageFormatException("Unexpected Huffman code");
}
if (done)
{
break;
}
int blah = zig;
zig = this.RefineNonZeroes(b, zig, zigEnd, val0, delta, unzigPtr);
if (zig > zigEnd)
{
throw new ImageFormatException($"Too many coefficients {zig} > {zigEnd}");
}
if (z != 0)
{
// b[Unzig[zig]] = z;
Block8x8F.SetScalarAt(b, unzigPtr[zig], z);
}
}
}
if (this.EobRun > 0)
{
this.EobRun--;
this.RefineNonZeroes(b, zig, zigEnd, -1, delta, unzigPtr);
}
}
/// <summary>
/// Refines non-zero entries of b in zig-zag order.
/// If <paramref name="nz" /> >= 0, the first <paramref name="nz" /> zero entries are skipped over.
/// </summary>
/// <param name="b">The block of coefficients</param>
/// <param name="zig">The zig-zag start index</param>
/// <param name="zigEnd">The zig-zag end index</param>
/// <param name="nz">The non-zero entry</param>
/// <param name="delta">The low transform offset</param>
/// <param name="unzigPtr">Pointer to the Jpeg Unzig data (data part of <see cref="UnzigData" />)</param>
/// <returns>The <see cref="int" /></returns>
private int RefineNonZeroes(Block8x8F* b, int zig, int zigEnd, int nz, int delta, int* unzigPtr)
{
for (; zig <= zigEnd; zig++)
{
int u = unzigPtr[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 = this.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;
this.MakeImage(scan.XNumberOfMCUs, scan.YNumberOfMCUs);
scan.ProcessBlocks(this);
}
/// <summary>

Loading…
Cancel
Save