private static void Decode1DScanline(ref BitReader bitReader, bool whiteIsZero, Span <byte> scanline) { int width = scanline.Length; // Process every code word in this scanline byte fillValue = whiteIsZero ? (byte)0 : (byte)255; CcittDecodingTable currentTable = CcittDecodingTable.WhiteInstance; CcittDecodingTable otherTable = CcittDecodingTable.BlackInstance; int unpacked = 0; while (true) { int runLength = currentTable.DecodeRun(ref bitReader); if ((uint)runLength > (uint)scanline.Length) { throw new InvalidOperationException(); } scanline.Slice(0, runLength).Fill(fillValue); scanline = scanline.Slice(runLength); unpacked += runLength; // Switch to the other color. fillValue = (byte)~fillValue; CcittHelper.SwapTable(ref currentTable, ref otherTable); if (unpacked == width) { // This line is fully unpacked. Should exit and process next line. break; } } }
private static void Decode2DScanline(ref BitReader bitReader, bool whiteIsZero, ReferenceScanline referenceScanline, Span <byte> scanline) { int width = scanline.Length; CcittDecodingTable currentTable = CcittDecodingTable.WhiteInstance; CcittDecodingTable otherTable = CcittDecodingTable.BlackInstance; CcittTwoDimensionalDecodingTable decodingTable = CcittTwoDimensionalDecodingTable.Instance; CcittTwoDimensionalCodeValue tableEntry; const int PeekCount = CcittTwoDimensionalDecodingTable.PeekCount; // 2D Encoding variables. int a0 = -1, a1, b1; byte fillByte = whiteIsZero ? (byte)0 : (byte)255; // Process every code word in this scanline int unpacked = 0; while (true) { // Read next code word and advance pass it. int value = (int)bitReader.Peek(PeekCount); // PeekCount = 12 // Special case handling for EOL. // Lucky we don't need to peek again for EOL, because PeekCount(12) is excatly the length of EOL code. if (value == 0b000000000001) { scanline.Fill(fillByte); break; } // Look up in the table and advance past this code. if (!decodingTable.TryLookup(value, out tableEntry)) { throw new InvalidDataException(); } bitReader.Advance(tableEntry.BitsRequired); // Update 2D Encoding variables. b1 = referenceScanline.FindB1(a0, fillByte); // Switch on the code word. switch (tableEntry.Type) { case CcittTwoDimensionalCodeType.Pass: int b2 = referenceScanline.FindB2(b1); scanline.Slice(unpacked, b2 - unpacked).Fill(fillByte); unpacked = b2; a0 = b2; break; case CcittTwoDimensionalCodeType.Horizontal: // Decode M(a0a1) int runLength = currentTable.DecodeRun(ref bitReader); if ((uint)runLength > (uint)(scanline.Length - unpacked)) { throw new InvalidOperationException(); } scanline.Slice(unpacked, runLength).Fill(fillByte); unpacked += runLength; fillByte = (byte)~fillByte; CcittHelper.SwapTable(ref currentTable, ref otherTable); // Decode M(a1a2) runLength = currentTable.DecodeRun(ref bitReader); if ((uint)runLength > (uint)(scanline.Length - unpacked)) { throw new InvalidOperationException(); } scanline.Slice(unpacked, runLength).Fill(fillByte); unpacked += runLength; fillByte = (byte)~fillByte; CcittHelper.SwapTable(ref currentTable, ref otherTable); // Prepare next a0 a0 = unpacked; break; case CcittTwoDimensionalCodeType.Vertical0: a1 = b1; scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); unpacked = a1; a0 = a1; fillByte = (byte)~fillByte; CcittHelper.SwapTable(ref currentTable, ref otherTable); break; case CcittTwoDimensionalCodeType.VerticalR1: a1 = b1 + 1; scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); unpacked = a1; a0 = a1; fillByte = (byte)~fillByte; CcittHelper.SwapTable(ref currentTable, ref otherTable); break; case CcittTwoDimensionalCodeType.VerticalR2: a1 = b1 + 2; scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); unpacked = a1; a0 = a1; fillByte = (byte)~fillByte; CcittHelper.SwapTable(ref currentTable, ref otherTable); break; case CcittTwoDimensionalCodeType.VerticalR3: a1 = b1 + 3; scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); unpacked = a1; a0 = a1; fillByte = (byte)~fillByte; CcittHelper.SwapTable(ref currentTable, ref otherTable); break; case CcittTwoDimensionalCodeType.VerticalL1: a1 = b1 - 1; scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); unpacked = a1; a0 = a1; fillByte = (byte)~fillByte; CcittHelper.SwapTable(ref currentTable, ref otherTable); break; case CcittTwoDimensionalCodeType.VerticalL2: a1 = b1 - 2; scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); unpacked = a1; a0 = a1; fillByte = (byte)~fillByte; CcittHelper.SwapTable(ref currentTable, ref otherTable); break; case CcittTwoDimensionalCodeType.VerticalL3: a1 = b1 - 3; scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); unpacked = a1; a0 = a1; fillByte = (byte)~fillByte; CcittHelper.SwapTable(ref currentTable, ref otherTable); break; default: throw new NotSupportedException("1D and 2D Extensions not supportted."); } // This line is fully unpacked. Should exit and process next line. if (unpacked == width) { break; } else if (unpacked > width) { throw new InvalidDataException(); } } }