private static int DecodeHuffmanCode(ref JpegBitReader reader, JpegHuffmanDecodingTable table) { int bits = reader.PeekBits(16, out int bitsRead); JpegHuffmanDecodingTable.Entry entry = table.Lookup(bits); bitsRead = Math.Min(entry.CodeSize, bitsRead); _ = reader.TryAdvanceBits(bitsRead, out _); return(entry.SymbolValue); }
private static int Receive(ref JpegBitReader reader, int length) { Debug.Assert(length > 0); if (!reader.TryReadBits(length, out int value, out bool isMarkerEncountered)) { if (isMarkerEncountered) { ThrowInvalidDataException("Expect raw data from bit stream. Yet a marker is encountered."); } ThrowInvalidDataException("The bit stream ended prematurely."); } return(value); }
private static void CopyBlockBaseline(ref JpegBitReader reader, ref JpegWriter writer, JpegTranscodeComponent component) { Debug.Assert(!(component.DcTable is null)); Debug.Assert(!(component.DcEncodingTable is null)); Debug.Assert(!(component.AcTable is null)); Debug.Assert(!(component.AcEncodingTable is null)); // DC int symbol = DecodeHuffmanCode(ref reader, component.DcTable !); component.DcEncodingTable !.GetCode(symbol, out ushort code, out int codeLength); writer.WriteBits(code, codeLength); if (symbol != 0) { int received = Receive(ref reader, symbol); writer.WriteBits((uint)received, symbol); } // AC for (int i = 1; i < 64;) { symbol = DecodeHuffmanCode(ref reader, component.AcTable !); component.AcEncodingTable !.GetCode(symbol, out code, out codeLength); writer.WriteBits(code, codeLength); int r = symbol >> 4; symbol &= 15; if (symbol != 0) { i += r + 1; int received = Receive(ref reader, symbol); writer.WriteBits((uint)received, symbol); } else { if (r == 0) { break; } i += 16; } } }
private static void ProcessBlockBaseline(ref JpegBitReader reader, JpegTranscodeComponent component) { Debug.Assert(!(component.DcTable is null)); Debug.Assert(!(component.DcTableBuilder is null)); Debug.Assert(!(component.AcTable is null)); Debug.Assert(!(component.AcTableBuilder is null)); // DC int t = DecodeHuffmanCode(ref reader, component.DcTable !); component.DcTableBuilder !.IncrementCodeCount(t); if (t != 0) { Receive(ref reader, t); } // AC for (int i = 1; i < 64;) { int s = DecodeHuffmanCode(ref reader, component.AcTable !); component.AcTableBuilder !.IncrementCodeCount(s); int r = s >> 4; s &= 15; if (s != 0) { i += r; i++; Receive(ref reader, s); } else { if (r == 0) { break; } i += 16; } } }
private void CopyScanBaseline(ref JpegReader reader, ref JpegWriter writer, JpegScanHeader scanHeader) { JpegFrameHeader frameHeader = _frameHeader.GetValueOrDefault(); if (scanHeader.Components is null) { throw new InvalidOperationException(); } // Compute maximum sampling factor byte maxHorizontalSampling = 1; byte maxVerticalSampling = 1; foreach (JpegFrameComponentSpecificationParameters currentFrameComponent in frameHeader.Components !) { maxHorizontalSampling = Math.Max(maxHorizontalSampling, currentFrameComponent.HorizontalSamplingFactor); maxVerticalSampling = Math.Max(maxVerticalSampling, currentFrameComponent.VerticalSamplingFactor); } // Resolve each component JpegTranscodeComponent[] components = new JpegTranscodeComponent[scanHeader.NumberOfComponents]; for (int i = 0; i < scanHeader.NumberOfComponents; i++) { JpegScanComponentSpecificationParameters scanComponenet = scanHeader.Components[i]; int componentIndex = 0; JpegFrameComponentSpecificationParameters?frameComponent = null; for (int j = 0; j < frameHeader.NumberOfComponents; j++) { JpegFrameComponentSpecificationParameters currentFrameComponent = frameHeader.Components[j]; if (scanComponenet.ScanComponentSelector == currentFrameComponent.Identifier) { componentIndex = j; frameComponent = currentFrameComponent; } } if (frameComponent is null) { throw new InvalidDataException(); } components[i] = new JpegTranscodeComponent { ComponentIndex = componentIndex, HorizontalSamplingFactor = frameComponent.GetValueOrDefault().HorizontalSamplingFactor, VerticalSamplingFactor = frameComponent.GetValueOrDefault().VerticalSamplingFactor, DcTable = GetHuffmanTable(true, scanComponenet.DcEntropyCodingTableSelector), AcTable = GetHuffmanTable(false, scanComponenet.AcEntropyCodingTableSelector), DcEncodingTable = _encodingTables.GetTable(true, scanComponenet.DcEntropyCodingTableSelector), AcEncodingTable = _encodingTables.GetTable(false, scanComponenet.AcEntropyCodingTableSelector) }; } // Prepare int mcusPerLine = (frameHeader.SamplesPerLine + 8 * maxHorizontalSampling - 1) / (8 * maxHorizontalSampling); int mcusPerColumn = (frameHeader.NumberOfLines + 8 * maxVerticalSampling - 1) / (8 * maxVerticalSampling); JpegBitReader bitReader = new JpegBitReader(reader.RemainingBytes); int mcusBeforeRestart = _restartInterval; bool eoiReached = false; writer.EnterBitMode(); for (int rowMcu = 0; rowMcu < mcusPerColumn && !eoiReached; rowMcu++) { for (int colMcu = 0; colMcu < mcusPerLine && !eoiReached; colMcu++) { foreach (JpegTranscodeComponent component in components) { int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; for (int y = 0; y < v; y++) { for (int x = 0; x < h; x++) { CopyBlockBaseline(ref bitReader, ref writer, component); } } } if (_restartInterval > 0 && (--mcusBeforeRestart) == 0) { bitReader.AdvanceAlignByte(); JpegMarker marker = bitReader.TryReadMarker(); if (marker == JpegMarker.EndOfImage) { eoiReached = true; break; } if (!marker.IsRestartMarker()) { throw new InvalidOperationException("Expect restart marker."); } mcusBeforeRestart = _restartInterval; writer.ExitBitMode(); writer.WriteMarker(marker); writer.EnterBitMode(); } } } bitReader.AdvanceAlignByte(); writer.ExitBitMode(); int bytesConsumed = reader.RemainingByteCount - bitReader.RemainingBits / 8; if (eoiReached) { bytesConsumed -= 2; } else if (bitReader.TryPeekMarker() != 0) { if (!bitReader.TryPeekMarker().IsRestartMarker()) { bytesConsumed -= 2; } } reader.TryAdvance(bytesConsumed); }