示例#1
0
        public virtual int Identify(bool loadQuantizationTables)
        {
            if (_inputBuffer.IsEmpty)
            {
                throw new InvalidOperationException("Input buffer is not specified.");
            }

            JpegReader reader = new JpegReader(_inputBuffer);

            // Reset frame header
            _frameHeader = default;

            bool toContinue = true;

            while (toContinue && !reader.IsEmpty)
            {
                // Read next marker
                if (!reader.TryReadMarker(out JpegMarker marker))
                {
                    ThrowInvalidDataException(reader.ConsumedByteCount, "No marker found.");
                }

                toContinue = ProcessMarkerForIdentification(marker, ref reader, loadQuantizationTables);
            }

            if (_frameHeader is null)
            {
                throw new InvalidOperationException("Frame header was not found.");
            }

            return(reader.ConsumedByteCount);
        }
示例#2
0
 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;
 }
示例#3
0
        protected virtual bool ProcessMarkerForIdentification(JpegMarker marker, ref JpegReader reader, bool loadQuantizationTables)
        {
            switch (marker)
            {
            case JpegMarker.StartOfImage:
                break;

            case JpegMarker.StartOfFrame0:
            case JpegMarker.StartOfFrame1:
            case JpegMarker.StartOfFrame2:
            case JpegMarker.StartOfFrame3:
            case JpegMarker.StartOfFrame9:
            case JpegMarker.StartOfFrame10:
            case JpegMarker.StartOfFrame5:
            case JpegMarker.StartOfFrame6:
            case JpegMarker.StartOfFrame7:
            case JpegMarker.StartOfFrame11:
            case JpegMarker.StartOfFrame13:
            case JpegMarker.StartOfFrame14:
            case JpegMarker.StartOfFrame15:
                StartOfFrame = marker;
                ProcessFrameHeader(ref reader, false, false);
                break;

            case JpegMarker.StartOfScan:
                ProcessScanHeader(ref reader, true);
                break;

            case JpegMarker.DefineRestartInterval:
                ProcessDefineRestartInterval(ref reader);
                break;

            case JpegMarker.DefineQuantizationTable:
                ProcessDefineQuantizationTable(ref reader, loadQuantizationTables);
                break;

            case JpegMarker.DefineRestart0:
            case JpegMarker.DefineRestart1:
            case JpegMarker.DefineRestart2:
            case JpegMarker.DefineRestart3:
            case JpegMarker.DefineRestart4:
            case JpegMarker.DefineRestart5:
            case JpegMarker.DefineRestart6:
            case JpegMarker.DefineRestart7:
                break;

            case JpegMarker.EndOfImage:
                return(false);

            default:
                ProcessOtherMarker(ref reader);
                break;
            }

            return(true);
        }
示例#4
0
 private static void SkipMarkerData(ref JpegReader reader)
 {
     if (!reader.TryReadLength(out ushort length))
     {
         ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data when reading segment length.");
     }
     if (!reader.TryAdvance(length))
     {
         ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data when reading segment content.");
     }
 }
示例#5
0
 private void ProcessOtherMarker(ref JpegReader reader)
 {
     if (!reader.TryReadLength(out ushort length))
     {
         ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data when reading segment length.");
         return;
     }
     if (!reader.TryAdvance(length))
     {
         ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data reached.");
         return;
     }
 }
示例#6
0
 private void ProcessDefineQuantizationTable(ref JpegReader reader)
 {
     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;
     }
     ProcessDefineQuantizationTable(buffer, reader.ConsumedByteCount - length);
 }
示例#7
0
        private static ReadOnlySequence <byte> CopyMarkerData(ref JpegReader reader, ref JpegWriter writer)
        {
            if (!reader.TryReadLength(out ushort length))
            {
                ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data when reading segment length.");
            }

            if (!reader.TryReadBytes(length, out ReadOnlySequence <byte> buffer))
            {
                ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data when reading segment content.");
            }
            writer.WriteLength(length);
            writer.WriteBytes(buffer);
            return(buffer);
        }
示例#8
0
 private JpegScanHeader ProcessScanHeader(ref JpegReader reader, bool metadataOnly)
 {
     if (!reader.TryReadLength(out ushort length))
     {
         ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data when reading segment length.");
     }
     if (!reader.TryReadBytes(length, out ReadOnlySequence <byte> buffer))
     {
         ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data when reading segment content.");
     }
     if (!JpegScanHeader.TryParse(buffer, metadataOnly, out JpegScanHeader scanHeader, out int bytesConsumed))
     {
         ThrowInvalidDataException(reader.ConsumedByteCount - length + bytesConsumed, "Failed to parse scan header.");
     }
     return(scanHeader);
 }
示例#9
0
        private void ProcessDefineRestartInterval(ref JpegReader reader)
        {
            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) || buffer.Length < 2)
            {
                ThrowInvalidDataException(reader.ConsumedByteCount, "Unexpected end of input data when reading segment content.");
                return;
            }
            Span <byte> local = stackalloc byte[2];

            buffer.Slice(0, 2).CopyTo(local);
            _restartInterval = BinaryPrimitives.ReadUInt16BigEndian(local);
        }
示例#10
0
        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);
        }
示例#11
0
        public void Scan()
        {
            if (_inputBuffer.IsEmpty)
            {
                throw new InvalidOperationException("Input buffer is not specified.");
            }

            JpegReader reader = new JpegReader(_inputBuffer);

            // Reset frame header
            _frameHeader = default;

            bool scanRead          = false;
            bool endOfImageReached = false;

            while (!endOfImageReached && !reader.IsEmpty)
            {
                // Read next marker
                if (!reader.TryReadMarker(out JpegMarker marker))
                {
                    ThrowInvalidDataException(reader.ConsumedByteCount, "No marker found.");
                    return;
                }

                switch (marker)
                {
                case JpegMarker.StartOfImage:
                    break;

                case JpegMarker.StartOfFrame0:
                    ProcessFrameHeader(ref reader, false, false);
                    break;

                case JpegMarker.StartOfFrame1:
                    ProcessFrameHeader(ref reader, false, false);
                    break;

                case JpegMarker.StartOfFrame3:
                case JpegMarker.StartOfFrame5:
                case JpegMarker.StartOfFrame6:
                case JpegMarker.StartOfFrame7:
                case JpegMarker.StartOfFrame9:
                case JpegMarker.StartOfFrame10:
                case JpegMarker.StartOfFrame11:
                case JpegMarker.StartOfFrame13:
                case JpegMarker.StartOfFrame14:
                case JpegMarker.StartOfFrame15:
                    ThrowInvalidDataException(reader.ConsumedByteCount, $"This type of JPEG stream is not supported ({marker}).");
                    return;

                case JpegMarker.DefineHuffmanTable:
                    ProcessDefineHuffmanTable(ref reader);
                    break;

                case JpegMarker.DefineQuantizationTable:
                    ProcessDefineQuantizationTable(ref reader);
                    break;

                case JpegMarker.DefineRestartInterval:
                    ProcessDefineRestartInterval(ref reader);
                    break;

                case JpegMarker.StartOfScan:
                    JpegScanHeader scanHeader = ProcessScanHeader(ref reader, false);
                    ProcessScanBaseline(ref reader, scanHeader);
                    scanRead = true;
                    break;

                case JpegMarker.DefineRestart0:
                case JpegMarker.DefineRestart1:
                case JpegMarker.DefineRestart2:
                case JpegMarker.DefineRestart3:
                case JpegMarker.DefineRestart4:
                case JpegMarker.DefineRestart5:
                case JpegMarker.DefineRestart6:
                case JpegMarker.DefineRestart7:
                    break;

                case JpegMarker.EndOfImage:
                    endOfImageReached = true;
                    break;

                default:
                    ProcessOtherMarker(ref reader);
                    break;
                }
            }

            if (!scanRead)
            {
                ThrowInvalidDataException(reader.ConsumedByteCount, "No image data is read.");
                return;
            }
        }
示例#12
0
        public void Optimize(bool strip = true)
        {
            if (_encodingTables.IsEmpty)
            {
                throw new InvalidOperationException();
            }

            IBufferWriter <byte> bufferWriter = _output ?? throw new InvalidOperationException();

            JpegReader reader = new JpegReader(_inputBuffer);
            var        writer = new JpegWriter(bufferWriter, _minimumBufferSegmentSize);

            bool eoiReached               = false;
            bool huffmanTableWritten      = false;
            bool quantizationTableWritten = false;

            while (!eoiReached && !reader.IsEmpty)
            {
                if (!reader.TryReadMarker(out JpegMarker marker))
                {
                    ThrowInvalidDataException(reader.ConsumedByteCount, "No marker found.");
                    return;
                }

                switch (marker)
                {
                case JpegMarker.StartOfImage:
                    writer.WriteMarker(marker);
                    break;

                case JpegMarker.App0:
                case JpegMarker.StartOfFrame0:
                case JpegMarker.StartOfFrame1:
                    writer.WriteMarker(marker);
                    CopyMarkerData(ref reader, ref writer);
                    break;

                case JpegMarker.StartOfFrame2:
                    ProcessFrameHeader(ref reader, false, true);
                    throw new InvalidDataException("Progressive JPEG is not supported currently.");

                case JpegMarker.StartOfFrame3:
                case JpegMarker.StartOfFrame5:
                case JpegMarker.StartOfFrame6:
                case JpegMarker.StartOfFrame7:
                case JpegMarker.StartOfFrame9:
                case JpegMarker.StartOfFrame10:
                case JpegMarker.StartOfFrame11:
                case JpegMarker.StartOfFrame13:
                case JpegMarker.StartOfFrame14:
                case JpegMarker.StartOfFrame15:
                    ThrowInvalidDataException(reader.ConsumedByteCount, $"This type of JPEG stream is not supported ({marker}).");
                    return;

                case JpegMarker.DefineHuffmanTable:
                    if (!huffmanTableWritten)
                    {
                        WriteHuffmanTables(ref writer);
                        huffmanTableWritten = true;
                    }
                    break;

                case JpegMarker.DefineQuantizationTable:
                    if (!quantizationTableWritten)
                    {
                        WriteQuantizationTables(ref writer);
                        quantizationTableWritten = true;
                    }
                    break;

                case JpegMarker.StartOfScan:
                    writer.WriteMarker(marker);
                    ReadOnlySequence <byte> buffer = CopyMarkerData(ref reader, ref writer);
                    if (!JpegScanHeader.TryParse(buffer, false, out JpegScanHeader scanHeader, out _))
                    {
                        ThrowInvalidDataException(reader.ConsumedByteCount - (int)buffer.Length, "Failed to parse scan header.");
                    }
                    CopyScanBaseline(ref reader, ref writer, scanHeader);
                    break;

                case JpegMarker.DefineRestart0:
                case JpegMarker.DefineRestart1:
                case JpegMarker.DefineRestart2:
                case JpegMarker.DefineRestart3:
                case JpegMarker.DefineRestart4:
                case JpegMarker.DefineRestart5:
                case JpegMarker.DefineRestart6:
                case JpegMarker.DefineRestart7:
                    writer.WriteMarker(marker);
                    break;

                case JpegMarker.EndOfImage:
                    writer.WriteMarker(JpegMarker.EndOfImage);
                    eoiReached = true;
                    break;

                default:
                    if (strip)
                    {
                        SkipMarkerData(ref reader);
                    }
                    else
                    {
                        writer.WriteMarker(marker);
                        CopyMarkerData(ref reader, ref writer);
                    }
                    break;
                }
            }

            writer.Flush();
        }