/// <inheritdoc />
        public void Decompress(TiffDecompressionContext context, ReadOnlyMemory <byte> input, Memory <byte> output)
        {
            var inflater = new Inflater(noHeader: false);

            Span <byte> destination = output.Span;

            while (!destination.IsEmpty)
            {
                int bytesWritten = inflater.Inflate(destination);

                if (inflater.IsFinished)
                {
                    break;
                }

                if (inflater.IsNeedingInput)
                {
                    if (input.IsEmpty)
                    {
                        throw new InvalidDataException();
                    }

                    inflater.SetInput(input);
                    input = default;
                }
                else if (bytesWritten == 0)
                {
                    throw new InvalidDataException();
                }

                destination = destination.Slice(bytesWritten);
            }
        }
Example #2
0
        /// <inheritdoc />
        public void Decompress(TiffDecompressionContext context, ReadOnlyMemory <byte> input, Memory <byte> output)
        {
            ReadOnlySpan <byte> inputSpan           = input.Span;
            Span <byte>         scanlinesBufferSpan = output.Span;

            if (inputSpan.IsEmpty)
            {
                return;
            }

            byte first = inputSpan[0];

            if (first == 0)
            {
                DecompressLeastSignificantBitFirst(inputSpan, scanlinesBufferSpan);
            }
            else if (first == 128)
            {
                DecompressMostSignificantBitFirst(inputSpan, scanlinesBufferSpan);
            }
            else
            {
                throw new InvalidDataException();
            }
        }
        public void Decompress(TiffDecompressionContext context, ReadOnlyMemory <byte> input, Memory <byte> output)
        {
            // Copy frame header
            JpegFrameHeader frameHeader = _frameHeader;

            frameHeader = new JpegFrameHeader(frameHeader.SamplePrecision, (ushort)context.ImageSize.Height, (ushort)context.ImageSize.Width, frameHeader.NumberOfComponents, frameHeader.Components);

            var decoder = new JpegDecoder();

            decoder.StartOfFrame = JpegMarker.StartOfFrame0;
            decoder.MemoryPool   = context.MemoryPool;
            decoder.SetFrameHeader(frameHeader);
            decoder.SetRestartInterval(_restartInterval);

            foreach (ComponentInfo componentInfo in _components)
            {
                decoder.SetQuantizationTable(componentInfo.QuantizationTable);
                decoder.SetHuffmanTable(componentInfo.DcTable);
                decoder.SetHuffmanTable(componentInfo.AcTable);
            }

            var outputWriter = new JpegBuffer8BitOutputWriter(context.ImageSize.Width, context.SkippedScanlines, context.SkippedScanlines + context.RequestedScanlines, decoder.NumberOfComponents, output);

            decoder.SetOutputWriter(outputWriter);

            var reader = new JpegReader(input);

            decoder.ProcessScan(ref reader, _scanHeader);
        }
Example #4
0
        /// <inheritdoc />
        public void Decompress(TiffDecompressionContext context, ReadOnlyMemory <byte> input, Memory <byte> output)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            int bytesPerScanline                    = context.BytesPerScanline;
            ReadOnlySpan <byte> inputSpan           = input.Span;
            Span <byte>         scanlinesBufferSpan = output.Span;

            int totalScanlines = context.SkippedScanlines + context.RequestedScanlines;

            for (int i = 0; i < totalScanlines; i++)
            {
                if (scanlinesBufferSpan.Length < bytesPerScanline)
                {
                    throw new ArgumentException("destination too short.", nameof(output));
                }
                Span <byte> scanline = scanlinesBufferSpan.Slice(0, bytesPerScanline);
                scanlinesBufferSpan = scanlinesBufferSpan.Slice(bytesPerScanline);

                int unpacked = 0;
                while (unpacked < bytesPerScanline)
                {
                    sbyte n = (sbyte)inputSpan[0];
                    inputSpan = inputSpan.Slice(1);
                    if (n >= 0)
                    {
                        inputSpan.Slice(0, n + 1).CopyTo(scanline);
                        inputSpan = inputSpan.Slice(n + 1);
                        scanline  = scanline.Slice(n + 1);
                        unpacked += n + 1;
                    }
                    else if (n != -128)
                    {
                        byte v = inputSpan[0];
                        inputSpan = inputSpan.Slice(1);
                        scanline.Slice(0, 1 - n).Fill(v);
                        scanline  = scanline.Slice(1 - n);
                        unpacked += 1 - n;
                    }
                }
            }
        }
        /// <inheritdoc />
        public void Decompress(TiffDecompressionContext context, ReadOnlyMemory <byte> input, Memory <byte> output)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.PhotometricInterpretation != TiffPhotometricInterpretation.WhiteIsZero && context.PhotometricInterpretation != TiffPhotometricInterpretation.BlackIsZero)
            {
                throw new NotSupportedException("T6 compression does not support this photometric interpretation.");
            }

            if (context.BitsPerSample.Count != 1 || context.BitsPerSample[0] != 1)
            {
                throw new NotSupportedException("Unsupported bits per sample.");
            }

            ReadOnlySpan <byte> inputSpan           = input.Span;
            Span <byte>         scanlinesBufferSpan = output.Span;

            bool whiteIsZero = context.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero;
            int  width       = context.ImageSize.Width;
            int  height      = context.SkippedScanlines + context.RequestedScanlines;
            var  bitReader   = new BitReader(inputSpan, higherOrderBitsFirst: _higherOrderBitsFirst);

            ReferenceScanline referenceScanline = new ReferenceScanline(whiteIsZero, width);

            // Process every scanline
            for (int i = 0; i < height; i++)
            {
                Span <byte> scanline = scanlinesBufferSpan.Slice(0, width);
                scanlinesBufferSpan = scanlinesBufferSpan.Slice(width);

                Decode2DScanline(ref bitReader, whiteIsZero, referenceScanline, scanline);

                referenceScanline = new ReferenceScanline(whiteIsZero, scanline);
            }
        }
 /// <inheritdoc />
 public void Decompress(TiffDecompressionContext context, ReadOnlyMemory <byte> input, Memory <byte> output)
 {
     input.CopyTo(output);
 }
        /// <inheritdoc />
        public void Decompress(TiffDecompressionContext context, ReadOnlyMemory <byte> input, Memory <byte> output)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.PhotometricInterpretation != TiffPhotometricInterpretation.WhiteIsZero && context.PhotometricInterpretation != TiffPhotometricInterpretation.BlackIsZero)
            {
                throw new NotSupportedException("ThunderScan compression does not support this photometric interpretation.");
            }

            if (context.BitsPerSample.Count != 1 || context.BitsPerSample[0] != 4)
            {
                throw new NotSupportedException("Unsupported bits per sample.");
            }

            ReadOnlySpan <sbyte> twoBitDiffs   = MemoryMarshal.Cast <byte, sbyte>(TwoBitDiffDecodeTable);
            ReadOnlySpan <sbyte> threeBitDiffs = MemoryMarshal.Cast <byte, sbyte>(ThreeBitDiffDecodeTable);

            ReadOnlySpan <byte> inputSpan = input.Span;
            OutputWriter        writer    = new OutputWriter(output.Span, context.ImageSize.Width, context.ImageSize.Height);

            int   delta;
            uint  lastPixel   = 0;
            ulong buffer      = 0;
            uint  bufferCount = 0;

            for (int i = 0; i < inputSpan.Length; i++)
            {
                uint inputByte = inputSpan[i];

                switch (inputByte & 0b11_000000)
                {
                case 0b00_000000:
                    for (uint j = 0; j < inputByte; j++)
                    {
                        if (bufferCount == 16)
                        {
                            writer.Write(buffer, 16);
                            bufferCount = 0;
                        }
                        buffer = (buffer << 4) | lastPixel;
                        bufferCount++;
                    }
                    break;

                case 0b01_000000:
                    if (bufferCount > (16 - 3))
                    {
                        writer.Write(buffer, bufferCount);
                        bufferCount = 0;
                    }
                    if ((delta = (int)((inputByte >> 4) & 0b11)) != 0b10)
                    {
                        lastPixel = (uint)((int)lastPixel + twoBitDiffs[delta]) & 0b1111;
                        buffer    = (buffer << 4) | lastPixel;
                        bufferCount++;
                    }
                    if ((delta = (int)((inputByte >> 2) & 0b11)) != 0b10)
                    {
                        lastPixel = (uint)((int)lastPixel + twoBitDiffs[delta]) & 0b1111;
                        buffer    = (buffer << 4) | lastPixel;
                        bufferCount++;
                    }
                    if ((delta = (int)(inputByte & 0b11)) != 0b10)
                    {
                        lastPixel = (uint)((int)lastPixel + twoBitDiffs[delta]) & 0b1111;
                        buffer    = (buffer << 4) | lastPixel;
                        bufferCount++;
                    }
                    break;

                case 0b10_000000:
                    if (bufferCount > (16 - 2))
                    {
                        writer.Write(buffer, bufferCount);
                        bufferCount = 0;
                    }
                    if ((delta = (int)((inputByte >> 3) & 0b111)) != 0b100)
                    {
                        lastPixel = (uint)((int)lastPixel + threeBitDiffs[delta]) & 0b1111;
                        buffer    = (buffer << 4) | lastPixel;
                        bufferCount++;
                    }
                    if ((delta = (int)(inputByte & 0b111)) != 0b100)
                    {
                        lastPixel = (uint)((int)lastPixel + threeBitDiffs[delta]) & 0b1111;
                        buffer    = (buffer << 4) | lastPixel;
                        bufferCount++;
                    }
                    break;

                case 0b11_000000:
                    if (bufferCount == 16)
                    {
                        writer.Write(buffer, 16);
                        bufferCount = 0;
                    }
                    lastPixel = inputByte & 0b1111;
                    buffer    = (buffer << 4) | lastPixel;
                    bufferCount++;
                    break;
                }
            }

            if (bufferCount != 0)
            {
                writer.Write(buffer, bufferCount);
            }
            if (!writer.RemainingSpan.IsEmpty)
            {
                writer.RemainingSpan.Clear();
            }
        }
        /// <inheritdoc />
        public void Decompress(TiffDecompressionContext context, ReadOnlyMemory <byte> input, Memory <byte> output)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.PhotometricInterpretation != TiffPhotometricInterpretation.WhiteIsZero && context.PhotometricInterpretation != TiffPhotometricInterpretation.BlackIsZero)
            {
                throw new NotSupportedException("ThunderScan compression does not support this photometric interpretation.");
            }

            if (context.BitsPerSample.Count != 1 || context.BitsPerSample[0] != 2)
            {
                throw new NotSupportedException("Unsupported bits per sample.");
            }

            int width            = context.ImageSize.Width;
            int height           = context.SkippedScanlines + context.RequestedScanlines;
            int bytesPerScanline = context.BytesPerScanline;

            ReadOnlySpan <byte> inputSpan  = input.Span;
            Span <byte>         outputSpan = output.Span;

            for (int row = 0; row < height; row++)
            {
                if (inputSpan.IsEmpty)
                {
                    ThrowMoreDataIsExpected();
                }

                Span <byte> scanline = outputSpan.Slice(0, bytesPerScanline);
                outputSpan = outputSpan.Slice(bytesPerScanline);

                int leadingByte = inputSpan[0];
                if (leadingByte == 0x00)
                {
                    // Literal row
                    inputSpan = inputSpan.Slice(1);
                    if (inputSpan.Length < scanline.Length)
                    {
                        ThrowMoreDataIsExpected();
                    }

                    inputSpan.Slice(0, scanline.Length).CopyTo(scanline);
                    inputSpan = inputSpan.Slice(scanline.Length);
                }
                else if (leadingByte == 0x40)
                {
                    // Literal span
                    if (inputSpan.Length < 5)
                    {
                        ThrowMoreDataIsExpected();
                    }

                    int offset = BinaryPrimitives.ReadUInt16BigEndian(inputSpan.Slice(1));
                    int count  = BinaryPrimitives.ReadUInt16BigEndian(inputSpan.Slice(3));
                    inputSpan = inputSpan.Slice(5);
                    if (inputSpan.Length < count)
                    {
                        ThrowMoreDataIsExpected();
                    }

                    if (offset > scanline.Length || (offset + count) > scanline.Length)
                    {
                        throw new InvalidDataException("Incorrect offset and count for literal span.");
                    }

                    scanline.Slice(0, offset).Fill(0xff);
                    inputSpan.Slice(0, count).CopyTo(scanline.Slice(offset, count));
                    scanline.Slice(offset + count).Fill(0xff);

                    inputSpan = inputSpan.Slice(count);
                }
                else
                {
                    // RLE
                    var writer = new RunLengthWriter(scanline, width);
                    while (!writer.IsEndOfLine)
                    {
                        if (inputSpan.IsEmpty)
                        {
                            ThrowMoreDataIsExpected();
                        }
                        writer.WriteRunLength(inputSpan[0]);
                        inputSpan = inputSpan.Slice(1);
                    }
                }
            }
        }
Example #9
0
        /// <inheritdoc />
        public void Decompress(TiffDecompressionContext context, ReadOnlyMemory <byte> input, Memory <byte> output)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.PhotometricInterpretation != TiffPhotometricInterpretation.WhiteIsZero && context.PhotometricInterpretation != TiffPhotometricInterpretation.BlackIsZero)
            {
                throw new NotSupportedException("T4 compression does not support this photometric interpretation.");
            }

            if (context.BitsPerSample.Count != 1 || context.BitsPerSample[0] != 1)
            {
                throw new NotSupportedException("Unsupported bits per sample.");
            }

            ReadOnlySpan <byte> inputSpan           = input.Span;
            Span <byte>         scanlinesBufferSpan = output.Span;

            bool whiteIsZero = context.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero;
            int  width       = context.ImageSize.Width;
            int  height      = context.SkippedScanlines + context.RequestedScanlines;
            var  bitReader   = new BitReader(inputSpan, higherOrderBitsFirst: _higherOrderBitsFirst);

            // Skip the first EOL code
            if (bitReader.Peek(12) == 0b000000000001) // EOL code is 000000000001 (12bits).
            {
                bitReader.Advance(12);
            }
            else if (bitReader.Peek(16) == 1) // Or EOL code is zero filled.
            {
                bitReader.Advance(16);
            }

            // Process every scanline
            for (int i = 0; i < height; i++)
            {
                if (scanlinesBufferSpan.Length < width)
                {
                    throw new InvalidDataException();
                }
                Span <byte> scanline = scanlinesBufferSpan.Slice(0, width);
                scanlinesBufferSpan = scanlinesBufferSpan.Slice(width);

                // 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;
                CcittCodeValue tableEntry;
                while (true)
                {
                    uint value = bitReader.Peek(16);
                    if (!currentTable.TryLookup(value, out tableEntry))
                    {
                        throw new InvalidDataException();
                    }

                    if (tableEntry.IsEndOfLine)
                    {
                        // fill the rest of scanline
                        scanline.Fill(fillValue);
                        bitReader.Advance(tableEntry.BitsRequired);
                        break;
                    }

                    // Check to see whether we are encountering a "filled" EOL ?
                    if ((value & 0b1111111111110000) == 0)
                    {
                        // Align to 8 bits
                        int filledBits = 8 - (bitReader.ConsumedBits + 12) % 8;
                        if (bitReader.Peek(filledBits) != 0)
                        {
                            throw new InvalidDataException();
                        }

                        // Confirm it is indeed an EOL code.
                        value = bitReader.Read(filledBits + 12);
                        if (value == 0b000000000001)
                        {
                            // fill the rest of scanline
                            scanline.Fill(fillValue);
                            break;
                        }

                        throw new InvalidDataException();
                    }

                    // Process normal code.
                    int runLength = tableEntry.RunLength;
                    scanline.Slice(0, runLength).Fill(fillValue);
                    scanline  = scanline.Slice(runLength);
                    unpacked += runLength;
                    bitReader.Advance(tableEntry.BitsRequired);

                    // Terminating code is met. Switch to the other color.
                    if (tableEntry.IsTerminatingCode)
                    {
                        fillValue = (byte)~fillValue;
                        CcittHelper.SwapTable(ref currentTable, ref otherTable);

                        // This line is fully unpacked. Should exit and process next line.
                        if (unpacked == width)
                        {
                            // If this is the last line of the image, we don't process any code after this.
                            if (i == height - 1)
                            {
                                break;
                            }

                            // Is the next code word EOL ?
                            value = bitReader.Peek(16);
                            if ((value >> 4) == 0b000000000001)
                            {
                                // Skip the EOL code
                                bitReader.Advance(12);
                                break;
                            }

                            // Maybe the EOL is zero filled? First align to 8 bits
                            int filledBits = 8 - (bitReader.ConsumedBits + 12) % 8;
                            if (bitReader.Peek(filledBits) != 0)
                            {
                                bitReader.AdvanceAlignByte();
                                break;
                            }

                            // Confirm it is indeed an EOL code.
                            value = bitReader.Peek(filledBits + 12);
                            if (value == 0b000000000001)
                            {
                                bitReader.Advance(filledBits + 12);
                            }

                            bitReader.AdvanceAlignByte();
                            break;
                        }
                    }
                }
            }
        }
Example #10
0
        /// <inheritdoc />
        public void Decompress(TiffDecompressionContext context, ReadOnlyMemory <byte> input, Memory <byte> output)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.PhotometricInterpretation != TiffPhotometricInterpretation.WhiteIsZero && context.PhotometricInterpretation != TiffPhotometricInterpretation.BlackIsZero)
            {
                throw new NotSupportedException("T4 compression does not support this photometric interpretation.");
            }

            if (context.BitsPerSample.Count != 1 || context.BitsPerSample[0] != 1)
            {
                throw new NotSupportedException("Unsupported bits per sample.");
            }

            ReadOnlySpan <byte> inputSpan           = input.Span;
            Span <byte>         scanlinesBufferSpan = output.Span;

            bool whiteIsZero = context.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero;
            int  width       = context.ImageSize.Width;
            int  height      = context.SkippedScanlines + context.RequestedScanlines;
            var  bitReader   = new BitReader(inputSpan, _higherOrderBitsFirst);

            ReferenceScanline referenceScanline = default;

            // Process every scanline
            for (int i = 0; i < height; i++)
            {
                if (scanlinesBufferSpan.Length < width)
                {
                    throw new InvalidDataException();
                }
                Span <byte> scanline = scanlinesBufferSpan.Slice(0, width);
                scanlinesBufferSpan = scanlinesBufferSpan.Slice(width);

                // Read the first EOL.
                if (bitReader.Peek(12) == 0b000000000001) // EOL code is 000000000001 (12bits).
                {
                    bitReader.Advance(12);
                }
                else // Or EOL code is zero filled.
                {
                    // Align to 8 bits
                    int filledBits = 8 - (bitReader.ConsumedBits + 12) % 8;
                    if (bitReader.Peek(filledBits) != 0)
                    {
                        throw new InvalidDataException();
                    }

                    // Confirm it is indeed an EOL code.
                    int value = (int)bitReader.Read(filledBits + 12);
                    if (value != 0b000000000001)
                    {
                        throw new InvalidDataException();
                    }
                }

                // Determine the type of this line (1d vs 2d)
                bool is1D = bitReader.Read(1) != 0;

                if (is1D)
                {
                    Decode1DScanline(ref bitReader, whiteIsZero, scanline);
                }
                else
                {
                    if (referenceScanline.IsEmpty)
                    {
                        throw new InvalidDataException();
                    }
                    Decode2DScanline(ref bitReader, whiteIsZero, referenceScanline, scanline);
                }
                referenceScanline = new ReferenceScanline(whiteIsZero, scanline);
            }
        }
        /// <inheritdoc />
        public void Decompress(TiffDecompressionContext context, ReadOnlyMemory <byte> input, Memory <byte> output)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.PhotometricInterpretation != TiffPhotometricInterpretation.WhiteIsZero && context.PhotometricInterpretation != TiffPhotometricInterpretation.BlackIsZero)
            {
                throw new NotSupportedException("Modified Huffman compression does not support this photometric interpretation.");
            }

            if (context.BitsPerSample.Count != 1 || context.BitsPerSample[0] != 1)
            {
                throw new NotSupportedException("Unsupported bits per sample.");
            }

            ReadOnlySpan <byte> inputSpan           = input.Span;
            Span <byte>         scanlinesBufferSpan = output.Span;

            bool whiteIsZero = context.PhotometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero;
            int  width       = context.ImageSize.Width;
            int  height      = context.SkippedScanlines + context.RequestedScanlines;
            var  bitReader   = new BitReader(inputSpan, higherOrderBitsFirst: _higherOrderBitsFirst);

            // Process every scanline
            for (int i = 0; i < height; i++)
            {
                if (scanlinesBufferSpan.Length < width)
                {
                    throw new InvalidDataException();
                }
                Span <byte> scanline = scanlinesBufferSpan.Slice(0, width);
                scanlinesBufferSpan = scanlinesBufferSpan.Slice(width);

                // 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)
                {
                    if (!currentTable.TryLookup(bitReader.Peek(16), out CcittCodeValue tableEntry))
                    {
                        throw new InvalidDataException();
                    }

                    if (tableEntry.IsEndOfLine)
                    {
                        // EOL code is not used in modified huffman algorithm
                        throw new InvalidDataException();
                    }

                    // Process normal code.
                    int runLength = tableEntry.RunLength;
                    scanline.Slice(0, runLength).Fill(fillValue);
                    scanline  = scanline.Slice(runLength);
                    unpacked += runLength;
                    bitReader.Advance(tableEntry.BitsRequired);

                    // Terminating code is met. Switch to the other color.
                    if (tableEntry.IsTerminatingCode)
                    {
                        fillValue = (byte)~fillValue;
                        CcittHelper.SwapTable(ref currentTable, ref otherTable);

                        // This line is fully unpacked. Should exit and process next line.
                        if (unpacked == width)
                        {
                            break;
                        }
                    }
                }

                bitReader.AdvanceAlignByte();
            }
        }
        /// <inheritdoc />
        public void Decompress(TiffDecompressionContext context, ReadOnlyMemory <byte> input, Memory <byte> output)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            // Identify this block
            JpegDecoder decoder = Interlocked.Exchange(ref _decoder, null) ?? LoadJpegDecoder();

            decoder.MemoryPool = context.MemoryPool;
            decoder.SetInput(input);
            decoder.Identify();

            // Validate we are capable of decoding this.
            TiffSize outputBufferSize = context.ImageSize;

            if (decoder.Width < outputBufferSize.Width || decoder.Height < outputBufferSize.Height)
            {
                throw new InvalidDataException("Image dimension is too small.");
            }

            // Check number of components
            if (decoder.NumberOfComponents != _numberOfComponents)
            {
                throw new InvalidDataException($"Expect {_numberOfComponents} components, but got {decoder.NumberOfComponents} components in the JPEG stream.");
            }

            JpegBlockOutputWriter outputWriter;

            if (decoder.Precision == 8)
            {
                if (context.BitsPerSample.GetFirstOrDefault() != 8)
                {
                    throw new InvalidDataException("Precision of 8 bit is not expected.");
                }
                outputWriter = new JpegBuffer8BitOutputWriter(outputBufferSize.Width, context.SkippedScanlines, context.SkippedScanlines + context.RequestedScanlines, decoder.NumberOfComponents, output);
            }
            else if (decoder.Precision < 8)
            {
                if (context.BitsPerSample.GetFirstOrDefault() != 8)
                {
                    throw new InvalidDataException($"Precision of {decoder.Precision} bit is not expected.");
                }
                outputWriter = new JpegBufferAny8BitOutputWriter(outputBufferSize.Width, context.SkippedScanlines, context.SkippedScanlines + context.RequestedScanlines, decoder.NumberOfComponents, decoder.Precision, output);
            }
            else if (decoder.Precision <= 16)
            {
                if (context.BitsPerSample.GetFirstOrDefault() != 16)
                {
                    throw new InvalidDataException($"Precision of {decoder.Precision} bit is not expected.");
                }
                outputWriter = new JpegBufferAny16BitOutputWriter(outputBufferSize.Width, context.SkippedScanlines, context.SkippedScanlines + context.RequestedScanlines, decoder.NumberOfComponents, decoder.Precision, output);
            }
            else
            {
                throw new InvalidDataException($"Precision of {decoder.Precision} bit is not expected.");
            }

            // Decode
            decoder.SetOutputWriter(outputWriter);
            decoder.Decode();

            // Reset state
            decoder.ResetInput();
            decoder.ResetHeader();
            decoder.ResetOutputWriter();

            // Cache the instances
            Interlocked.CompareExchange(ref _decoder, decoder, null);
        }