/// <summary>
 /// Initializes a new instance of the <see cref="OrigJpegDecoderCore" /> class.
 /// </summary>
 /// <param name="configuration">The configuration.</param>
 /// <param name="options">The options.</param>
 public OrigJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options)
 {
     this.IgnoreMetadata     = options.IgnoreMetadata;
     this.configuration      = configuration ?? Configuration.Default;
     this.HuffmanTrees       = OrigHuffmanTree.CreateHuffmanTrees();
     this.QuantizationTables = new Block8x8F[MaxTq + 1];
     this.Temp = new byte[2 * Block8x8F.Size];
 }
Beispiel #2
0
        /// <summary>
        /// Read metadata from stream and read the blocks in the scans into <see cref="OrigComponent.SpectralBlocks"/>.
        /// </summary>
        /// <param name="stream">The stream</param>
        /// <param name="metadataOnly">Whether to decode metadata only.</param>
        public void ParseStream(Stream stream, bool metadataOnly = false)
        {
            this.MetaData       = new ImageMetaData();
            this.InputStream    = stream;
            this.InputProcessor = new InputProcessor(stream, this.Temp);

            if (!metadataOnly)
            {
                this.HuffmanTrees       = OrigHuffmanTree.CreateHuffmanTrees();
                this.QuantizationTables = new Block8x8F[MaxTq + 1];
            }

            // Check for the Start Of Image marker.
            this.InputProcessor.ReadFull(this.Temp, 0, 2);

            if (this.Temp[0] != OrigJpegConstants.Markers.XFF || this.Temp[1] != OrigJpegConstants.Markers.SOI)
            {
                throw new ImageFormatException("Missing SOI marker.");
            }

            // Process the remaining segments until the End Of Image marker.
            bool processBytes = true;

            // we can't currently short circute progressive images so don't try.
            while (processBytes)
            {
                this.InputProcessor.ReadFull(this.Temp, 0, 2);

                if (this.InputProcessor.ReachedEOF)
                {
                    // We've reached the end of the stream.
                    processBytes = false;
                }

                while (this.Temp[0] != 0xff)
                {
                    // Strictly speaking, this is a format error. However, libjpeg is
                    // liberal in what it accepts. As of version 9, next_marker in
                    // jdmarker.c treats this as a warning (JWRN_EXTRANEOUS_DATA) and
                    // continues to decode the stream. Even before next_marker sees
                    // extraneous data, jpeg_fill_bit_buffer in jdhuff.c reads as many
                    // bytes as it can, possibly past the end of a scan's data. It
                    // effectively puts back any markers that it overscanned (e.g. an
                    // "\xff\xd9" EOI marker), but it does not put back non-marker data,
                    // and thus it can silently ignore a small number of extraneous
                    // non-marker bytes before next_marker has a chance to see them (and
                    // print a warning).
                    // We are therefore also liberal in what we accept. Extraneous data
                    // is silently ignore
                    // This is similar to, but not exactly the same as, the restart
                    // mechanism within a scan (the RST[0-7] markers).
                    // Note that extraneous 0xff bytes in e.g. SOS data are escaped as
                    // "\xff\x00", and so are detected a little further down below.
                    this.Temp[0] = this.Temp[1];
                    this.Temp[1] = this.InputProcessor.ReadByte();
                }

                byte marker = this.Temp[1];
                if (marker == 0)
                {
                    // Treat "\xff\x00" as extraneous data.
                    continue;
                }

                while (marker == 0xff)
                {
                    // Section B.1.1.2 says, "Any marker may optionally be preceded by any
                    // number of fill bytes, which are bytes assigned code X'FF'".
                    this.InputProcessor.ReadByteUnsafe(out marker);

                    if (this.InputProcessor.ReachedEOF)
                    {
                        // We've reached the end of the stream.
                        processBytes = false;
                        break;
                    }
                }

                // End Of Image.
                if (marker == OrigJpegConstants.Markers.EOI)
                {
                    break;
                }

                if (marker >= OrigJpegConstants.Markers.RST0 && marker <= OrigJpegConstants.Markers.RST7)
                {
                    // Figures B.2 and B.16 of the specification suggest that restart markers should
                    // only occur between Entropy Coded Segments and not after the final ECS.
                    // However, some encoders may generate incorrect JPEGs with a final restart
                    // marker. That restart marker will be seen here instead of inside the ProcessSOS
                    // method, and is ignored as a harmless error. Restart markers have no extra data,
                    // so we check for this before we read the 16-bit length of the segment.
                    continue;
                }

                // Read the 16-bit length of the segment. The value includes the 2 bytes for the
                // length itself, so we subtract 2 to get the number of remaining bytes.
                this.InputProcessor.ReadFullUnsafe(this.Temp, 0, 2);
                int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2;
                if (remaining < 0)
                {
                    throw new ImageFormatException("Short segment length.");
                }

                switch (marker)
                {
                case OrigJpegConstants.Markers.SOF0:
                case OrigJpegConstants.Markers.SOF1:
                case OrigJpegConstants.Markers.SOF2:
                    this.IsProgressive = marker == OrigJpegConstants.Markers.SOF2;
                    this.ProcessStartOfFrameMarker(remaining, metadataOnly);

                    break;

                case OrigJpegConstants.Markers.DHT:
                    if (metadataOnly)
                    {
                        this.InputProcessor.Skip(remaining);
                    }
                    else
                    {
                        this.ProcessDefineHuffmanTablesMarker(remaining);
                    }

                    break;

                case OrigJpegConstants.Markers.DQT:
                    if (metadataOnly)
                    {
                        this.InputProcessor.Skip(remaining);
                    }
                    else
                    {
                        this.ProcessDefineQuantizationTablesMarker(remaining);
                    }

                    break;

                case OrigJpegConstants.Markers.SOS:
                    if (!metadataOnly)
                    {
                        this.ProcessStartOfScanMarker(remaining);
                        if (this.InputProcessor.ReachedEOF)
                        {
                            // If unexpected EOF reached. We can stop processing bytes as we now have the image data.
                            processBytes = false;
                        }
                    }
                    else
                    {
                        // It's highly unlikely that APPn related data will be found after the SOS marker
                        // We should have gathered everything we need by now.
                        processBytes = false;
                    }

                    break;

                case OrigJpegConstants.Markers.DRI:
                    if (metadataOnly)
                    {
                        this.InputProcessor.Skip(remaining);
                    }
                    else
                    {
                        this.ProcessDefineRestartIntervalMarker(remaining);
                    }

                    break;

                case OrigJpegConstants.Markers.APP0:
                    this.ProcessApplicationHeaderMarker(remaining);
                    break;

                case OrigJpegConstants.Markers.APP1:
                    this.ProcessApp1Marker(remaining);
                    break;

                case OrigJpegConstants.Markers.APP2:
                    this.ProcessApp2Marker(remaining);
                    break;

                case OrigJpegConstants.Markers.APP14:
                    this.ProcessApp14Marker(remaining);
                    break;

                default:
                    if ((marker >= OrigJpegConstants.Markers.APP0 && marker <= OrigJpegConstants.Markers.APP15) ||
                        marker == OrigJpegConstants.Markers.COM)
                    {
                        this.InputProcessor.Skip(remaining);
                    }

                    break;
                }
            }

            this.InitDerivedMetaDataProperties();
        }