|
|
|
@ -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>
|
|
|
|
|