private void ProcessFrameHeader(ref JpegReader reader, bool metadataOnly, bool overrideAllowed)
 {
     // Read length field
     if (!reader.TryReadLength(out ushort length))
     {
         ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data when reading segment length.");
         return;
     }
     if (!reader.TryReadBytes(length, out ReadOnlySequence <byte> buffer))
     {
         ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data when reading segment content.");
         return;
     }
     if (!JpegFrameHeader.TryParse(buffer, metadataOnly, out JpegFrameHeader frameHeader, out int bytesConsumed))
     {
         ThrowInvalidDataException(reader.ConsumedByteCount - length + bytesConsumed, "Failed to parse frame header.");
         return;
     }
     if (!overrideAllowed && _frameHeader.HasValue)
     {
         ThrowInvalidDataException(reader.ConsumedByteCount, "Multiple frame is not supported.");
         return;
     }
     _frameHeader = frameHeader;
 }
        public static bool TryParse(ReadOnlySequence <byte> buffer, bool metadataOnly, out JpegFrameHeader frameHeader, out int bytesConsumed)
        {
            if (buffer.IsSingleSegment)
            {
#if NO_READONLYSEQUENCE_FISTSPAN
                return(TryParse(buffer.First.Span, metadataOnly, out frameHeader, out bytesConsumed));
#else
                return(TryParse(buffer.FirstSpan, metadataOnly, out frameHeader, out bytesConsumed));
#endif
            }

            bytesConsumed = 0;

            if (buffer.Length < 6)
            {
                frameHeader = default;
                return(false);
            }

            Span <byte> local = stackalloc byte[6];
            buffer.Slice(0, 6).CopyTo(local);

            byte   numberOfComponenets = local[5];
            ushort samplesPerLine      = (ushort)(local[4] | (local[3] << 8));
            ushort numberOfLines       = (ushort)(local[2] | (local[1] << 8));
            byte   precision           = local[0];

            buffer         = buffer.Slice(6);
            bytesConsumed += 6;

            if (buffer.Length < 3 * numberOfComponenets)
            {
                frameHeader = default;
                return(false);
            }

            if (metadataOnly)
            {
                bytesConsumed += 3 * numberOfComponenets;
                frameHeader    = new JpegFrameHeader(precision, numberOfLines, samplesPerLine, numberOfComponenets, null);
                return(true);
            }

            JpegFrameComponentSpecificationParameters[] components = new JpegFrameComponentSpecificationParameters[numberOfComponenets];
            for (int i = 0; i < components.Length; i++)
            {
                if (!JpegFrameComponentSpecificationParameters.TryParse(buffer, out components[i]))
                {
                    frameHeader = default;
                    return(false);
                }
                buffer         = buffer.Slice(3);
                bytesConsumed += 3;
            }

            frameHeader = new JpegFrameHeader(precision, numberOfLines, samplesPerLine, numberOfComponenets, components);
            return(true);
        }
        public void Allocate(JpegFrameHeader frameHeader)
        {
            if (!(_bufferHandle is null))
            {
                throw new InvalidOperationException();
            }

            // Compute maximum sampling factor
            int maxHorizontalSampling = 1;
            int maxVerticalSampling   = 1;

            foreach (JpegFrameComponentSpecificationParameters currentFrameComponent in frameHeader.Components !)
            {
                maxHorizontalSampling = Math.Max(maxHorizontalSampling, currentFrameComponent.HorizontalSamplingFactor);
                maxVerticalSampling   = Math.Max(maxVerticalSampling, currentFrameComponent.VerticalSamplingFactor);
            }

            int horizontalBlockCount = (frameHeader.SamplesPerLine + 7) / 8;
            int verticalBlockCount   = (frameHeader.NumberOfLines + 7) / 8;

            ComponentAllocation[] componentAllocations = _components = new ComponentAllocation[frameHeader.NumberOfComponents];
            int index = 0;

            foreach (JpegFrameComponentSpecificationParameters component in frameHeader.Components)
            {
                int horizontalSubsamplingFactor = maxHorizontalSampling / component.HorizontalSamplingFactor;
                int verticalSubsamplingFactor   = maxVerticalSampling / component.VerticalSamplingFactor;

                int horizontalComponentBlockCount = (horizontalBlockCount + horizontalSubsamplingFactor - 1) / horizontalSubsamplingFactor;
                int verticalComponentBlockCount   = (verticalBlockCount + verticalSubsamplingFactor - 1) / verticalSubsamplingFactor;

                componentAllocations[index++] = new ComponentAllocation
                {
                    HorizontalComponentBlock    = horizontalComponentBlockCount,
                    VerticalComponentBlock      = verticalComponentBlockCount,
                    HorizontalSubsamplingFactor = horizontalSubsamplingFactor,
                    VerticalSubsamplingFactor   = verticalSubsamplingFactor,
                };
            }

            // allocate an additional block to act as a dummy buffer.
            index = 1;
            for (int i = 0; i < componentAllocations.Length; i++)
            {
                componentAllocations[i].ComponentBlockOffset = index;
                index += componentAllocations[i].HorizontalComponentBlock * componentAllocations[i].VerticalComponentBlock;
            }

            int length = index * Unsafe.SizeOf <JpegBlock8x8>();
            IMemoryOwner <byte> bufferHandle = _bufferHandle = _memoryPool.Rent(length);

            bufferHandle.Memory.Span.Slice(0, length).Clear();
        }
        public static bool TryParse(ReadOnlySpan <byte> buffer, bool metadataOnly, out JpegFrameHeader frameHeader, out int bytesConsumed)
        {
            bytesConsumed = 0;

            if (buffer.Length < 6)
            {
                frameHeader = default;
                return(false);
            }

            byte   numberOfComponenets = buffer[5];
            ushort samplesPerLine      = (ushort)(buffer[4] | (buffer[3] << 8));
            ushort numberOfLines       = (ushort)(buffer[2] | (buffer[1] << 8));
            byte   precision           = buffer[0];

            buffer         = buffer.Slice(6);
            bytesConsumed += 6;

            if (buffer.Length < 3 * numberOfComponenets)
            {
                frameHeader = default;
                return(false);
            }

            if (metadataOnly)
            {
                bytesConsumed += 3 * numberOfComponenets;
                frameHeader    = new JpegFrameHeader(precision, numberOfLines, samplesPerLine, numberOfComponenets, null);
                return(true);
            }

            JpegFrameComponentSpecificationParameters[] components = new JpegFrameComponentSpecificationParameters[numberOfComponenets];
            for (int i = 0; i < components.Length; i++)
            {
                if (!JpegFrameComponentSpecificationParameters.TryParse(buffer, out components[i]))
                {
                    frameHeader = default;
                    return(false);
                }
                buffer         = buffer.Slice(3);
                bytesConsumed += 3;
            }

            frameHeader = new JpegFrameHeader(precision, numberOfLines, samplesPerLine, numberOfComponenets, components);
            return(true);
        }
        public void Allocate(JpegFrameHeader frameHeader)
        {
            // Compute maximum sampling factor
            int maxHorizontalSampling = 1;
            int maxVerticalSampling   = 1;

            foreach (JpegFrameComponentSpecificationParameters currentFrameComponent in frameHeader.Components !)
            {
                maxHorizontalSampling = Math.Max(maxHorizontalSampling, currentFrameComponent.HorizontalSamplingFactor);
                maxVerticalSampling   = Math.Max(maxVerticalSampling, currentFrameComponent.VerticalSamplingFactor);
            }

            ComponentAllocation[] componentAllocations = _components = new ComponentAllocation[frameHeader.NumberOfComponents];
            int index = 0;

            foreach (JpegFrameComponentSpecificationParameters component in frameHeader.Components)
            {
                int horizontalSubsamplingFactor = maxHorizontalSampling / component.HorizontalSamplingFactor;
                int verticalSubsamplingFactor   = maxVerticalSampling / component.VerticalSamplingFactor;

                int width  = (frameHeader.SamplesPerLine + horizontalSubsamplingFactor - 1) / horizontalSubsamplingFactor;
                int height = (frameHeader.NumberOfLines + verticalSubsamplingFactor - 1) / verticalSubsamplingFactor;

                componentAllocations[index++] = new ComponentAllocation
                {
                    Width  = width,
                    Height = height,
                    HorizontalSubsamplingFactor = horizontalSubsamplingFactor,
                    VerticalSubsamplingFactor   = verticalSubsamplingFactor,
                };
            }

            index = 0;
            for (int i = 0; i < componentAllocations.Length; i++)
            {
                componentAllocations[i].ComponentSampleOffset = index;
                index += componentAllocations[i].Width * 16;
            }

            int length = index * Unsafe.SizeOf <short>();
            IMemoryOwner <byte> bufferHandle = _bufferHandle = _memoryPool.Rent(length);

            bufferHandle.Memory.Span.Slice(0, length).Clear();
        }
        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);
        }
 internal bool ShadowEquals(JpegFrameHeader other)
 {
     return(SamplePrecision == other.SamplePrecision && NumberOfLines == other.NumberOfLines && SamplesPerLine == other.SamplesPerLine && NumberOfComponents == other.NumberOfComponents);
 }