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);
        }