/// <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="bp">The <see cref="InputProcessor"/></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(ref InputProcessor bp, int zig, int nz, int delta) { var b = this.pointers.Block; for (; zig <= this.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; DecoderErrorCode errorCode = bp.DecodeBitUnsafe(out bit); if (!bp.CheckEOFEnsureNoError(errorCode)) { return(int.MinValue); } 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); }
/// <summary> /// Decodes a successive approximation refinement block, as specified in section G.1.2. /// </summary> /// <param name="bp">The <see cref="InputProcessor"/> instance</param> /// <param name="h">The Huffman tree</param> /// <param name="delta">The low transform offset</param> private void Refine(ref InputProcessor bp, 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; DecoderErrorCode errorCode = bp.DecodeBitUnsafe(out bit); if (!bp.CheckEOFEnsureNoError(errorCode)) { return; } 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 (this.eobRun == 0) { for (; zig <= this.zigEnd; zig++) { bool done = false; int z = 0; int val; DecoderErrorCode errorCode = bp.DecodeHuffmanUnsafe(ref h, out val); if (!bp.CheckEOF(errorCode)) { return; } int val0 = val >> 4; int val1 = val & 0x0f; switch (val1) { case 0: if (val0 != 0x0f) { this.eobRun = 1 << val0; if (val0 != 0) { errorCode = this.DecodeEobRun(val0, ref bp); if (!bp.CheckEOFEnsureNoError(errorCode)) { return; } } done = true; } break; case 1: z = delta; bool bit; errorCode = bp.DecodeBitUnsafe(out bit); if (!bp.CheckEOFEnsureNoError(errorCode)) { return; } if (!bit) { z = -z; } break; default: throw new ImageFormatException("Unexpected Huffman code"); } if (done) { break; } zig = this.RefineNonZeroes(ref bp, zig, val0, delta); if (bp.UnexpectedEndOfStreamReached) { return; } 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 (this.eobRun > 0) { this.eobRun--; this.RefineNonZeroes(ref bp, zig, -1, delta); } }