public JpegMarker getNextMarker(bool allowskip) { if (!allowskip) { byte idL = input.ReadByte(); if (idL != 0xff) { throw new Exception("getNextMarker: (Noskip) Expected marker not found. Propably corrupt file."); } JpegMarker markL = (JpegMarker)input.ReadByte(); if (JpegMarker.M_FILL == markL || JpegMarker.M_STUFF == markL) { throw new Exception("getNextMarker: (Noskip) Expected marker, but found stuffed 00 or ff."); } return(markL); } input.skipToMarker(); byte id = input.ReadByte(); //TODO change //_ASSERTE(0xff == id); JpegMarker mark = (JpegMarker)input.ReadByte(); return(mark); }
public SOFInfo GetSOF(uint offset, uint size) { // JPEG is big endian if (Common.GetHostEndianness() == Endianness.Big) { input = new ImageBinaryReader(input.BaseStream, offset); } else { input = new ImageBinaryReaderBigEndian(input.BaseStream, offset); } if (GetNextMarker(false) != JpegMarker.SOI) { throw new RawDecoderException("Image did not start with SOI. Probably not an LJPEG"); } while (true) { JpegMarker m = GetNextMarker(true); if (m == JpegMarker.Sof3) { SOFInfo sof = new SOFInfo(); ParseSOF(sof); return(sof); } if (m == JpegMarker.EOI) { throw new RawDecoderException("Could not locate Start of Frame."); } } }
public JpegMarker GetNextMarker(bool allowskip) { if (!allowskip) { byte idL = input.ReadByte(); if (idL != 0xff) { throw new RawDecoderException("Expected marker not found. Probably corrupt file."); } JpegMarker markL = (JpegMarker)input.ReadByte(); if (JpegMarker.Fill == markL || JpegMarker.Stuff == markL) { throw new RawDecoderException("Expected marker, but found stuffed 00 or ff."); } return(markL); } input.SkipToMarker(); var id = input.ReadByte(); Debug.Assert(0xff == id); JpegMarker mark = (JpegMarker)input.ReadByte(); return(mark); }
/// <summary> /// Emit a SOF marker /// </summary> private void EmitSOF(JpegMarker code) { EmitMarker(code); Emit2Bytes((3 * m_cinfo.m_num_components) + 2 + 5 + 1); /* length */ /* Make sure image isn't bigger than SOF field can handle */ if (m_cinfo.jpeg_height > 65535 || m_cinfo.jpeg_width > 65535) { m_cinfo.ErrExit(JMessageCode.JERR_IMAGE_TOO_BIG, 65535); } EmitByte(m_cinfo.m_data_precision); Emit2Bytes(m_cinfo.jpeg_height); Emit2Bytes(m_cinfo.jpeg_width); EmitByte(m_cinfo.m_num_components); for (var ci = 0; ci < m_cinfo.m_num_components; ci++) { var componentInfo = m_cinfo.Component_info[ci]; EmitByte(componentInfo.Component_id); EmitByte((componentInfo.H_samp_factor << 4) + componentInfo.V_samp_factor); EmitByte(componentInfo.Quant_tbl_no); } }
public JpegBitReader(ReadOnlySequence <byte> data) { _data = data; _firstSpan = default; _buffer = 0; _bitsInBuffer = 0; _nextMarker = 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); }
public JpegMarker TryReadMarker() { if (_bitsInBuffer == 0) { JpegMarker marker = _nextMarker; _nextMarker = 0; return(marker); } return(0); }
public void WriteMarker(JpegMarker marker) { if (_bitMode) { throw new InvalidOperationException("When bit mode is enabled, you are not allowed to write bytes to the stream."); } EnsureBuffer(2); Span <byte> buffer = _buffer; buffer[1] = (byte)marker; buffer[0] = 0xff; _buffer = buffer.Slice(2); _bufferConsunmed += 2; }
public static Marker Load(Stream stream) { byte [] raw = new byte [2]; ushort length; if (stream.Length - stream.Position < 2) { return(null); } // FIXME there is a potential loop here. int read = Read(stream, raw, 0, 2); if (read < 2 || raw [0] != 0xff) { throw new System.Exception(System.String.Format("Invalid marker found {0}", raw [0])); } JpegMarker id = (JpegMarker)raw [1]; switch (id) { case JpegMarker.Soi: case JpegMarker.Eoi: case JpegMarker.Rst0: case JpegMarker.Rst1: case JpegMarker.Rst2: case JpegMarker.Rst3: case JpegMarker.Rst4: case JpegMarker.Rst5: case JpegMarker.Rst6: case JpegMarker.Rst7: case JpegMarker.Tem: case (JpegMarker)0: return(new Marker(id, null)); default: Read(stream, raw, 0, 2); length = FSpot.BitConverter.ToUInt16(raw, 0, false); byte [] data = new byte [length - 2]; Read(stream, data, 0, data.Length); return(new Marker(id, data)); } }
private int FillBuffer() { // Read until we have at least 32 bits in the buffer while (_bitsInBuffer < 32) { if (_nextMarker != 0) { return(_bitsInBuffer); } // Read next byte if (!TryReadNextByte(out byte byteRead)) { break; } if (byteRead == 0xff) { // A marker may be encountered, lets peek it out to see what it is. if (!TryPeekNextByte(out byteRead)) { // The stream ended prematurely break; } if (byteRead == 0xff) { // It is the padding byte, continue reading continue; } // It is not the padding byte, advance past it _ = TryReadNextByte(out _); if (byteRead != 0) { // It is a marker _nextMarker = (JpegMarker)byteRead; break; } // It a stuffed byte byteRead = 0xff; } // Put the byte read in the buffer _buffer = (_buffer << 8) | byteRead; _bitsInBuffer += 8; } return(_bitsInBuffer); }
public void getSOF(SOFInfo sof, UInt32 offset, UInt32 size) { if (!input.isValid(offset, size)) { throw new Exception("getSOF: Start offset plus size is longer than file. Truncated file."); } try { Endianness host_endian = Common.getHostEndianness(); // JPEG is big endian if (host_endian == Endianness.big) { input = new TIFFBinaryReader(input.BaseStream, offset, size); } else { input = new TIFFBinaryReaderRE(input.BaseStream, offset, size); } if (getNextMarker(false) != JpegMarker.M_SOI) { throw new Exception("getSOF: Image did not start with SOI. Probably not an LJPEG"); } while (true) { JpegMarker m = getNextMarker(true); if (JpegMarker.M_SOF3 == m) { parseSOF(sof); return; } if (JpegMarker.M_EOI == m) { throw new Exception("LJpegDecompressor: Could not locate Start of Frame."); } } } catch (IOException) { throw new Exception("LJpegDecompressor: IO exception, read outside file. Corrupt File."); } }
/*****************************************************************************/ /* *-------------------------------------------------------------- * * EmitSof -- * * Emit a SOF marker plus data. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ public void EmitSof(JpegMarker code) { EmitMarker(code); Emit2bytes((int)(3 * fSrcChannels + 2 + 5 + 1)); // length EmitByte((uint8)fSrcBitDepth); Emit2bytes((int)fSrcRows); Emit2bytes((int)fSrcCols); EmitByte((uint8)fSrcChannels); for (uint32 i = 0; i < fSrcChannels; i++) { EmitByte((uint8)i); EmitByte((uint8)((1 << 4) + 1)); // Not subsampled. EmitByte(0); // Tq shall be 0 for lossless. } }
public bool TryReadMarker(out JpegMarker marker) { Span <byte> buffer = stackalloc byte[2]; while (TryPeekToBuffer(buffer)) { byte b1 = buffer[0]; byte b2 = buffer[1]; if (b1 == (byte)JpegMarker.Padding) { if (b2 == (byte)JpegMarker.Padding) { _data = _data.Slice(1); continue; } else if (b2 == 0) { _data = _data.Slice(2); continue; } _data = _data.Slice(2); marker = (JpegMarker)b2; return(true); } SequencePosition?position = _data.PositionOf((byte)JpegMarker.Padding); if (!position.HasValue) { _data = default; marker = default; return(false); } _data = _data.Slice(position.GetValueOrDefault()); } marker = default; return(false); }
public static JpegScanDecoder?Create(JpegMarker sofMarker, JpegDecoder decoder, JpegFrameHeader header) { switch (sofMarker) { case JpegMarker.StartOfFrame0: case JpegMarker.StartOfFrame1: return(new JpegHuffmanBaselineScanDecoder(decoder, header)); case JpegMarker.StartOfFrame2: return(new JpegHuffmanProgressiveScanDecoder(decoder, header)); case JpegMarker.StartOfFrame3: return(new JpegHuffmanLosslessScanDecoder(decoder, header)); case JpegMarker.StartOfFrame9: return(new JpegArithmeticSequentialScanDecoder(decoder, header)); case JpegMarker.StartOfFrame10: return(new JpegArithmeticProgressiveScanDecoder(decoder, header)); default: return(null); } }
public void startDecoder(UInt32 offset, UInt32 size, UInt32 offsetX, UInt32 offsetY) { if (!input.isValid(offset, size)) { throw new Exception("startDecoder: Start offset plus size is longer than file. Truncated file."); } if ((int)offsetX >= mRaw.dim.x) { throw new Exception("startDecoder: X offset outside of image"); } if ((int)offsetY >= mRaw.dim.y) { throw new Exception("startDecoder: Y offset outside of image"); } offX = offsetX; offY = offsetY; try { Endianness host_endian = Common.getHostEndianness(); // JPEG is big endian if (host_endian == Endianness.big) { input = new TIFFBinaryReader(input.BaseStream, offset, size); } else { input = new TIFFBinaryReaderRE(input.BaseStream, offset, size); } if (getNextMarker(false) != JpegMarker.M_SOI) { throw new Exception("startDecoder: Image did not start with SOI. Probably not an LJPEG"); } // _RPT0(0,"Found SOI marker\n"); bool moreImage = true; while (moreImage) { JpegMarker m = getNextMarker(true); switch (m) { case JpegMarker.M_SOS: // _RPT0(0,"Found SOS marker\n"); parseSOS(); break; case JpegMarker.M_EOI: // _RPT0(0,"Found EOI marker\n"); moreImage = false; break; case JpegMarker.M_DHT: // _RPT0(0,"Found DHT marker\n"); parseDHT(); break; case JpegMarker.M_DQT: throw new Exception("LJpegDecompressor: Not a valid RAW file."); case JpegMarker.M_DRI: // _RPT0(0,"Found DRI marker\n"); break; case JpegMarker.M_APP0: // _RPT0(0,"Found APP0 marker\n"); break; case JpegMarker.M_SOF3: // _RPT0(0,"Found SOF 3 marker:\n"); parseSOF(frame); break; default: // Just let it skip to next marker // _RPT1(0, "Found marker:0x%x. Skipping\n", m); break; } } } catch (IOException) { throw; } }
public Marker FindMarker(JpegMarker id, string name) { return(FindMarker(new Signature(id, name))); }
public Signature(JpegMarker marker, string name) { Id = marker; Name = name; }
public Marker(JpegMarker type, byte [] data) { this.Type = type; this.Data = data; }
public Marker FindMarker (JpegMarker id, string name) { return FindMarker (new Signature (id, name)); }
/*****************************************************************************/ /* *-------------------------------------------------------------- * * EmitMarker -- * * Emit a marker code into the output stream. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ public void EmitMarker(JpegMarker mark) { EmitByte(0xFF); EmitByte((uint8)mark); }
////////////////////////////////////////////////////////////////////////// // Basic output routines. // // Note that we do not support suspension while writing a marker. // Therefore, an application using suspension must ensure that there is // enough buffer space for the initial markers (typ. 600-700 bytes) before // calling jpeg_start_compress, and enough space to write the trailing EOI // (a few bytes) before calling jpeg_finish_compress. Multipass compression // modes are not supported at all with suspension, so those two are the only // points where markers will be written. /// <summary> /// Emit a marker code /// </summary> private void EmitMarker(JpegMarker mark) { EmitByte(0xFF); EmitByte((int)mark); }
public override void ProcessScan(ref JpegReader reader, JpegScanHeader scanHeader) { if (scanHeader.Components is null) { throw new InvalidOperationException(); } if (Decoder.GetOutputWriter() is null) { throw new InvalidOperationException(); } // Resolve each component Span <JpegHuffmanDecodingComponent> components = _components.AsSpan(0, InitDecodeComponents(_frameHeader, scanHeader, _components)); foreach (JpegHuffmanDecodingComponent component in components) { if (component.DcTable is null) { ThrowInvalidDataException($"Huffman table of component {component.ComponentIndex} is not defined."); } } JpegPartialScanlineAllocator allocator = _allocator; int mcusPerLine = _mcusPerLine; int mcusPerColumn = _mcusPerColumn; // Prepare JpegBitReader bitReader = new JpegBitReader(reader.RemainingBytes); int restartInterval = _restartInterval; int mcusBeforeRestart = restartInterval; int predictor = scanHeader.StartOfSpectralSelection; int initialPrediction = 1 << (_frameHeader.SamplePrecision - scanHeader.SuccessiveApproximationBitPositionLow - 1); for (int rowMcu = 0; rowMcu < mcusPerColumn; rowMcu++) { for (int colMcu = 0; colMcu < mcusPerLine; colMcu++) { // Scan an interleaved mcu... process components in order foreach (JpegHuffmanDecodingComponent component in components) { int index = component.ComponentIndex; JpegHuffmanDecodingTable losslessTable = component.DcTable !; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; int offsetX = colMcu * h; int offsetY = rowMcu * v; for (int y = 0; y < v; y++) { Span <short> scanline = allocator.GetScanlineSpan(index, offsetY + y); Span <short> lastScanline = (y == 0 && rowMcu == 0) ? default : allocator.GetScanlineSpan(index, offsetY + y - 1); for (int x = 0; x < h; x++) { int diffValue = ReadSampleLossless(ref bitReader, losslessTable); // The one-dimensional horizontal predictor (prediction sample Ra) is used // for the first line of samples at the start of the scan and at the beginning of each restart interval if (rowMcu == 0 || (restartInterval > 0 && mcusBeforeRestart == restartInterval)) { // At the beginning of the first line and at the beginning of each restart interval the prediction value of 2^(P – 1) is used, where P is the input precision. // If the point transformation parameter (see A.4) is non-zero, the prediction value at the beginning of the first lines and the beginning of each restart interval is 2^(P – Pt – 1), where Pt is the value of the point transformation parameter. if (colMcu == 0 && x == 0) { diffValue += initialPrediction; } else { int ra = scanline[offsetX + x - 1]; int rb = y == 0 ? initialPrediction : lastScanline[offsetX + x]; int rc = y == 0 ? initialPrediction : lastScanline[offsetX + x - 1]; diffValue += predictor switch { 1 => ra, // Px = Ra 2 => rb, // Px = Rb 3 => rc, // Px = Rc 4 => ra + rb - rc, // Px = Ra + Rb – Rc 5 => ra + ((rb - rc) >> 1), // Px = Ra + (Rb – Rc)/2 6 => rb + ((ra - rc) >> 1), // Px = Rb + (Ra – Rc)/2 7 => (ra + rb) >> 1, // Px = (Ra + Rb)/2 _ => 0, // No prediction (See Annex J) }; } } // The sample from the line above(prediction sample Rb) is used at the start of each line, except for the first line else if (colMcu == 0) { diffValue += lastScanline[offsetX + x]; } else { diffValue += predictor switch { 1 => scanline[offsetX + x - 1], // Px = Ra 2 => lastScanline[offsetX + x], // Px = Rb 3 => lastScanline[offsetX + x - 1], // Px = Rc 4 => scanline[offsetX + x - 1] + lastScanline[offsetX + x] - lastScanline[offsetX + x - 1], // Px = Ra + Rb – Rc 5 => scanline[offsetX + x - 1] + ((lastScanline[offsetX + x] - lastScanline[offsetX + x - 1]) >> 1), // Px = Ra + (Rb – Rc)/2 6 => lastScanline[offsetX + x] + ((scanline[offsetX + x - 1] - lastScanline[offsetX + x - 1]) >> 1), // Px = Rb + (Ra – Rc)/2 7 => (scanline[offsetX + x - 1] + lastScanline[offsetX + x]) >> 1, // Px = (Ra + Rb)/2 _ => 0, // No prediction (See Annex J) }; } scanline[offsetX + x] = (short)diffValue; } } } // Handle restart if (restartInterval > 0 && (--mcusBeforeRestart) == 0) { bitReader.AdvanceAlignByte(); JpegMarker marker = bitReader.TryReadMarker(); if (marker == JpegMarker.EndOfImage) { int bytesConsumedEoi = reader.RemainingByteCount - bitReader.RemainingBits / 8; reader.TryAdvance(bytesConsumedEoi - 2); return; } if (!marker.IsRestartMarker()) { throw new InvalidOperationException("Expect restart marker."); } mcusBeforeRestart = restartInterval; } } // Flush allocator if (rowMcu == mcusPerColumn - 1) { foreach (JpegHuffmanDecodingComponent component in components) { allocator.FlushLastMcu(component.ComponentIndex, (rowMcu + 1) * component.VerticalSamplingFactor); } } else { foreach (JpegHuffmanDecodingComponent component in components) { allocator.FlushMcu(component.ComponentIndex, (rowMcu + 1) * component.VerticalSamplingFactor); } } } bitReader.AdvanceAlignByte(); int bytesConsumed = reader.RemainingByteCount - bitReader.RemainingBits / 8; if (bitReader.TryPeekMarker() != 0) { if (!bitReader.TryPeekMarker().IsRestartMarker()) { bytesConsumed -= 2; } } reader.TryAdvance(bytesConsumed); }
/// <summary> /// Get next marker in jpeg file (starts with 0xff) /// </summary> /// <param name="stream"></param> /// <param name="last_marker"></param> /// <param name="comment_correction"></param> /// <param name="ff_read"></param> /// <returns></returns> private static JpegMarker GetNextMarker(Stream stream, JpegMarker last_marker, int comment_correction, int ff_read) { int a = 0; JpegMarker marker; /* get marker byte, swallowing possible padding */ if (last_marker == JpegMarker.M_COM && comment_correction != 0) { /* some software does not count the length bytes of COM section */ /* one company doing so is very much envolved in JPEG... so we accept too */ /* by the way: some of those companies changed their code now... */ comment_correction = 2; } else { last_marker = 0; comment_correction = 0; } if (ff_read != 0) { a = 1; /* already read 0xff in filetype detection */ } do { marker = (JpegMarker)stream.ReadByte(); if (marker == JpegMarker.M_EOI) { return JpegMarker.M_EOI;/* we hit EOF */ } if (last_marker == JpegMarker.M_COM && comment_correction > 0) { if (marker != (JpegMarker)0xFF) { marker = (JpegMarker)0xFF; comment_correction--; } else { last_marker = JpegMarker.M_PSEUDO; /* stop skipping non 0xff for M_COM */ } } a++; } while (marker == (JpegMarker)0xff); if (a < 2) { return JpegMarker.M_EOI; /* at least one 0xff is needed before marker code */ } if (last_marker == JpegMarker.M_COM && comment_correction != 0) { return JpegMarker.M_EOI; /* ah illegal: char after COM section not 0xFF */ } return marker; }
public void StartDecoder(uint offset, uint size) { if (!input.IsValid(offset, size)) { throw new RawDecoderException("Start offset plus size is longer than file. Truncated file."); } if ((int)offX >= raw.fullSize.dim.width) { throw new RawDecoderException("X offset outside of image"); } if ((int)offY >= raw.fullSize.dim.height) { throw new RawDecoderException("Y offset outside of image"); } // JPEG is big endian if (Common.GetHostEndianness() == Endianness.Big) { input = new ImageBinaryReader(input.BaseStream, offset); } else { input = new ImageBinaryReaderBigEndian(input.BaseStream, offset); } if (GetNextMarker(false) != JpegMarker.SOI) { throw new RawDecoderException("Image did not start with SOI. Probably not an LJPEG"); } bool moreImage = true; while (moreImage) { JpegMarker m = GetNextMarker(true); switch (m) { case JpegMarker.DQT: throw new RawDecoderException("Not a valid RAW file."); case JpegMarker.DHT: // _RPT0(0,"Found DHT marker\n"); ParseDHT(); break; case JpegMarker.SOS: // _RPT0(0,"Found SOS marker\n"); ParseSOS(); break; case JpegMarker.Sof3: // _RPT0(0,"Found SOF 3 marker:\n"); ParseSOF(frame); break; case JpegMarker.EOI: // _RPT0(0,"Found EOI marker\n"); moreImage = false; break; case JpegMarker.DRI: // _RPT0(0,"Found DRI marker\n"); case JpegMarker.App0: // _RPT0(0,"Found APP0 marker\n"); default: // _RPT1(0, "Found marker:0x%x. Skipping\n", m); // Just let it skip to next marker break; } } }
public static bool IsRestartMarker(this JpegMarker marker) { return(JpegMarker.DefineRestart0 <= marker && marker <= JpegMarker.DefineRestart7); }
public Marker (JpegMarker type, byte [] data) { this.Type = type; this.Data = data; }
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); }
public override void ProcessScan(ref JpegReader reader, JpegScanHeader scanHeader) { JpegFrameHeader frameHeader = _frameHeader; JpegBlockOutputWriter?outputWriter = Decoder.GetOutputWriter(); if (frameHeader.Components is null) { ThrowInvalidDataException(); } if (scanHeader.Components is null) { ThrowInvalidDataException(); } if (outputWriter is null) { ThrowInvalidDataException(); } // Resolve each component Span <JpegArithmeticDecodingComponent> components = _components.AsSpan(0, InitDecodeComponents(frameHeader, scanHeader, _components)); foreach (JpegArithmeticDecodingComponent component in _components) { component.DcPredictor = 0; component.DcContext = 0; component.DcStatistics?.Reset(); component.AcStatistics?.Reset(); } Reset(); // Prepare int maxHorizontalSampling = _maxHorizontalSampling; int maxVerticalSampling = _maxVerticalSampling; int mcusBeforeRestart = _restartInterval; int mcusPerLine = _mcusPerLine; int mcusPerColumn = _mcusPerColumn; int levelShift = _levelShift; JpegBitReader bitReader = new JpegBitReader(reader.RemainingBytes); // DCT Block JpegBlock8x8F blockFBuffer = default; JpegBlock8x8F outputFBuffer = default; JpegBlock8x8F tempFBuffer = default; JpegBlock8x8 outputBuffer; for (int rowMcu = 0; rowMcu < mcusPerColumn; rowMcu++) { int offsetY = rowMcu * maxVerticalSampling; for (int colMcu = 0; colMcu < mcusPerLine; colMcu++) { int offsetX = colMcu * maxHorizontalSampling; // Scan an interleaved mcu... process components in order foreach (JpegArithmeticDecodingComponent component in components) { int index = component.ComponentIndex; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; int hs = component.HorizontalSubsamplingFactor; int vs = component.VerticalSubsamplingFactor; for (int y = 0; y < v; y++) { int blockOffsetY = (offsetY + y) * 8; for (int x = 0; x < h; x++) { // Read MCU outputBuffer = default; ReadBlock(ref bitReader, component, ref outputBuffer); // Dequantization DequantizeBlockAndUnZigZag(component.QuantizationTable, ref outputBuffer, ref blockFBuffer); // IDCT FastFloatingPointDCT.TransformIDCT(ref blockFBuffer, ref outputFBuffer, ref tempFBuffer); // Level shift ShiftDataLevel(ref outputFBuffer, ref outputBuffer, levelShift); // CopyToOutput WriteBlock(outputWriter, ref Unsafe.As <JpegBlock8x8, short>(ref outputBuffer), index, (offsetX + x) * 8, blockOffsetY, hs, vs); } } } // Handle restart if (_restartInterval > 0 && (--mcusBeforeRestart) == 0) { bitReader.AdvanceAlignByte(); JpegMarker marker = bitReader.TryReadMarker(); if (marker == JpegMarker.EndOfImage) { int bytesConsumedEoi = reader.RemainingByteCount - bitReader.RemainingBits / 8; reader.TryAdvance(bytesConsumedEoi - 2); return; } if (!marker.IsRestartMarker()) { throw new InvalidOperationException("Expect restart marker."); } mcusBeforeRestart = _restartInterval; foreach (JpegArithmeticDecodingComponent component in components) { component.DcPredictor = 0; component.DcContext = 0; component.DcStatistics?.Reset(); component.AcStatistics?.Reset(); } Reset(); } } } bitReader.AdvanceAlignByte(); int bytesConsumed = reader.RemainingByteCount - bitReader.RemainingBits / 8; if (bitReader.TryPeekMarker() != 0) { if (!bitReader.TryPeekMarker().IsRestartMarker()) { bytesConsumed -= 2; } } reader.TryAdvance(bytesConsumed); }
public Signature (JpegMarker marker, string name) { Id = marker; Name = name; }